home *** CD-ROM | disk | FTP | other *** search
Wrap
Text File | 2002-09-16 | 380.9 KB | 12,255 lines
dump tran master with no_log go set ANSI_NULLS off go use master go if exists (select * from sysobjects where name = 'xp_createqueue') execute dbo.sp_dropextendedproc 'xp_createqueue' go if exists (select * from sysobjects where name = 'xp_deletequeue') execute dbo.sp_dropextendedproc 'xp_deletequeue' go if exists (select * from sysobjects where name = 'xp_intersectbitmaps') execute dbo.sp_dropextendedproc 'xp_intersectbitmaps' go dump tran master with no_log go checkpoint go use master go if exists (select * from sysobjects where name = N'xp_SetSQLSecurity') execute dbo.sp_dropextendedproc N'xp_SetSQLSecurity' go print N'Creating extended stored procedure xp_SetSQLSecurity' exec sp_addextendedproc N'xp_SetSQLSecurity',N'xpstar.dll' go dump tran master with no_log go checkpoint go ------------------------------------------------------------------------------- --. qfe360814.sql ------------------------------------------------------------------------------- dump tran master with no_log go exec dbo.sp_configure 'allow updates',1 go reconfigure with override go set ANSI_NULLS off exec sp_MS_upd_sysobj_category 1 go -------------------------------------------------------------------------------- --. System objects (replsys.sql) -------------------------------------------------------------------------------- if exists (select * from sysobjects where type = 'FN' and name = N'fn_escapecmdshellsymbols' ) drop function system_function_schema.fn_escapecmdshellsymbols raiserror('Creating function fn_escapecmdshellsymbols', 0,1) with nowait go -- -- Name: fn_escapecmdshellsymbols -- -- Descriptions: This function returns an escaped version of a given string -- with carets ('^') added in front of all the special -- command shell symbols. In the W2K online help (Start-> -- Help->Index->Command Reference->Command Symbols...), the -- following symbols are listed as special command symbols: -- (N'%', N'<', N'>', N'|', N'&', N'(', N')', N'^', N'"') -- -- Parameter: @command_string nvarchar(4000) -- -- Example: select fn_escapecmdshellsymbols(N'The % quick < brown > dog | jumps & over ( the ) lazy ^ fox.') -- will return -- -- The ^% quick ^< brown ^> dog ^| jumps ^& over ^( the ^) lazy ^^ fox -- -- Security: This function is granted to public -- create function system_function_schema.fn_escapecmdshellsymbols( @command_string nvarchar(4000) ) returns nvarchar(4000) as begin declare @escaped_command_string nvarchar(4000), @curr_char nvarchar(1), @curr_char_index int select @escaped_command_string = N'', @curr_char = N'', @curr_char_index = 1 while @curr_char_index <= len(@command_string) begin select @curr_char = substring(@command_string, @curr_char_index, 1) if @curr_char in (N'%', N'<', N'>', N'|', N'&', N'(', N')', N'^', N'"') begin select @escaped_command_string = @escaped_command_string + N'^' end select @escaped_command_string = @escaped_command_string + @curr_char select @curr_char_index = @curr_char_index + 1 end return @escaped_command_string end go grant execute on system_function_schema.fn_escapecmdshellsymbols to public if exists (select * from sysobjects where type = 'FN' and name = N'fn_escapecmdshellsymbolsremovequotes' ) drop function system_function_schema.fn_escapecmdshellsymbolsremovequotes raiserror('Creating function fn_escapecmdshellsymbolsremovequotes', 0,1) with nowait go -- -- Name: fn_escapecmdshellsymbolsremovequotes -- -- Descriptions: The only difference between this function and -- fn_escapecmdshellsymbols is that "'s are removed from the -- resulting command line. -- -- Parameter: @command_string nvarchar(4000) -- -- Security: This function is granted to public -- create function system_function_schema.fn_escapecmdshellsymbolsremovequotes( @command_string nvarchar(4000) ) returns nvarchar(4000) as begin declare @escaped_command_string nvarchar(4000), @curr_char nvarchar(1), @curr_char_index int select @escaped_command_string = N'', @curr_char = N'', @curr_char_index = 1 while @curr_char_index <= len(@command_string) begin select @curr_char = substring(@command_string, @curr_char_index, 1) if @curr_char in (N'%', N'<', N'>', N'|', N'&', N'(', N')', N'^') begin select @escaped_command_string = @escaped_command_string + N'^' end if @curr_char <> '"' begin select @escaped_command_string = @escaped_command_string + @curr_char end select @curr_char_index = @curr_char_index + 1 end return @escaped_command_string end go grant execute on system_function_schema.fn_escapecmdshellsymbolsremovequotes to public if exists (select * from sysobjects where type = 'P ' and name = 'sp_MSget_file_existence') drop procedure sp_MSget_file_existence go print '' print 'Creating procedure sp_MSget_file_existence' go create procedure sp_MSget_file_existence ( @filename nvarchar(260), @exists bit = 0 output ) AS SET NOCOUNT ON DECLARE @command nvarchar(512) DECLARE @retcode int declare @echo_text nvarchar(20) select @echo_text = 'file_exists' /* ** The return code from xp_cmdshell is not a reliable way to check whether the file exists or ** not. It is always 0 on Win95 as long as xp_cmdshell succeeds. */ select @command = N'if exist "' + fn_escapecmdshellsymbolsremovequotes(@filename) collate database_default + N'" echo ' + @echo_text create table #text_ret(cmdoutput nvarchar(20) collate database_default null) insert into #text_ret exec @retcode = master..xp_cmdshell @command if @@error <> 0 or @retcode <> 0 return 1 if exists (select * from #text_ret where ltrim(rtrim(cmdoutput)) = @echo_text) select @exists = 1 else select @exists = 0 drop table #text_ret go EXEC dbo.sp_MS_marksystemobject sp_MSget_file_existence GO if exists (select * from sysobjects where type = 'P ' and name = 'sp_replicationoption') drop procedure sp_replicationoption go raiserror('Creating procedure sp_replicationoption', 0,1) go CREATE PROCEDURE sp_replicationoption ( @optname sysname, @value nvarchar(5), @security_mode int = 0, @login sysname = 'sa', @password sysname = NULL, @reserved nvarchar(20) = NULL ) AS DECLARE @optbit bit DECLARE @osql_path nvarchar(260) DECLARE @osql_cmd1 nvarchar (255) DECLARE @osql_cmd_full nvarchar (255) DECLARE @osql_for_nt int DECLARE @install_path nvarchar (255) DECLARE @retcode int DECLARE @undo_install nvarchar(20) DECLARE @no_scripts nvarchar(10) DECLARE @platform_nt binary SELECT @platform_nt = 0x1 if is_srvrolemember('sysadmin') <> 1 BEGIN RAISERROR (15232, 14, -1) RETURN (1) END SELECT @no_scripts = 'no_scripts' SELECT @undo_install = 'undo_install' IF db_name() <> 'master' BEGIN RAISERROR(5001, 16,-1) GOTO FAILURE END IF LOWER(@optname collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('transactional','merge') BEGIN RAISERROR(21014, 16, -1) GOTO FAILURE END IF LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true','false') BEGIN RAISERROR(14137,16,-1) GOTO FAILURE END IF LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'true' SELECT @optbit = 1 ELSE SELECT @optbit = 0 /* ** Check if the option is set as required already */ IF @reserved <> @undo_install AND EXISTS (SELECT * FROM MSreplication_options WHERE optname = @optname AND value = @optbit) BEGIN IF LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'true' RAISERROR (21015, 10, -1, @optname) ELSE RAISERROR (21016, 10, -1, @optname) GOTO FAILURE END /* Install replication */ IF @optbit = 1 BEGIN IF LOWER(@reserved collate SQL_Latin1_General_CP1_CS_AS) = @no_scripts GOTO NO_SCRIPTS -- Set the flag for platform IF (( platform() & @platform_nt = @platform_nt )) SELECT @osql_for_nt = 1 ELSE SELECT @osql_for_nt = 0 /* ** Get installation path -- osql client (TOOLS) path */ EXECUTE @retcode = master.dbo.sp_MSgettools_path @osql_path OUTPUT IF ( @@ERROR <> 0 ) OR ( @retcode <> 0 ) or ( @osql_path is NULL ) or ( @osql_path = '' ) BEGIN GOTO FAILURE END /* ** Get installation path -- instance specific (INSTALL) directory */ exec @retcode = master.dbo.sp_MSget_setup_paths @sql_path = @install_path output IF @@ERROR<> 0 OR @retcode <> 0 or @install_path is NULL or @install_path='' BEGIN GOTO FAILURE END /* ** Install replcom.sql and repltran.sql */ IF @security_mode = 1 begin SELECT @osql_cmd1 = '"' + fn_escapecmdshellsymbolsremovequotes(@osql_path) collate database_default + '\binn\osql" -E ' if serverproperty('instancename') is not null SELECT @osql_cmd1 = @osql_cmd1 + ' -S"' + fn_escapecmdshellsymbols(@@SERVERNAME) collate database_default + '" ' end ELSE -- cannot specify -S w/ -E for local execution, SID does not map SELECT @osql_cmd1 = '"' + fn_escapecmdshellsymbolsremovequotes(@osql_path) collate database_default + '\binn\osql" -U"' + fn_escapecmdshellsymbols(@login) collate database_default + '" -P"' + fn_escapecmdshellsymbols(isnull(@password,'')) collate database_default + '" -S"' + fn_escapecmdshellsymbols(@@SERVERNAME) collate database_default + '" ' select @osql_cmd1 = @osql_cmd1 + '-l30 -t30 ' -- Install replcom.sql -- Only apply replcom.sql if it was not applied before. -- '-b' option will make osql stop at errors and return error code -- We must use this option. IF NOT EXISTS (SELECT * FROM MSreplication_options WHERE value = 1) BEGIN -- Initialize the Command IF (@osql_for_nt = 1) SELECT @osql_cmd_full = '" ' ELSE SELECT @osql_cmd_full = ' ' SELECT @osql_cmd_full = @osql_cmd_full + @osql_cmd1 + ' -dmaster' + ' -b ' + ' -i' + '"' + fn_escapecmdshellsymbolsremovequotes(@install_path) collate database_default + '\install\replcom.sql"' + ' -o' + '"' + fn_escapecmdshellsymbolsremovequotes(@install_path) collate database_default + '\install\replcom.out"' IF (@osql_for_nt = 1) SELECT @osql_cmd_full = @osql_cmd_full + ' "' EXEC @retcode = master..xp_cmdshell @osql_cmd_full IF @@ERROR<> 0 OR @retcode <> 0 BEGIN RAISERROR (14113, 16, -1, @osql_cmd_full, 'replcom.out') GOTO UNDO_INSTALL END END IF LOWER(@optname collate SQL_Latin1_General_CP1_CS_AS) = 'transactional' BEGIN -- Install repltran.sql IF (@osql_for_nt = 1) SELECT @osql_cmd_full = '" ' ELSE SELECT @osql_cmd_full = ' ' SELECT @osql_cmd_full = @osql_cmd_full + @osql_cmd1 + ' -dmaster' + ' -b ' + ' -i' + '"' + fn_escapecmdshellsymbolsremovequotes(@install_path) collate database_default + '\install\repltran.sql"' + ' -o' + '"' + fn_escapecmdshellsymbolsremovequotes(@install_path) collate database_default + '\install\repltran.out"' IF (@osql_for_nt = 1) SELECT @osql_cmd_full = @osql_cmd_full + ' "' EXEC @retcode = master..xp_cmdshell @osql_cmd_full IF @@ERROR<> 0 OR @retcode <> 0 BEGIN RAISERROR (14113, 16, -1, @osql_cmd_full, 'repltran.out') GOTO UNDO_INSTALL END END IF LOWER(@optname collate SQL_Latin1_General_CP1_CS_AS) = 'merge' BEGIN -- Install replmerg.sql IF (@osql_for_nt = 1) SELECT @osql_cmd_full = '" ' ELSE SELECT @osql_cmd_full = ' ' SELECT @osql_cmd_full = @osql_cmd_full + @osql_cmd1 + ' -dmaster' + ' -b ' + ' -i' + '"' + fn_escapecmdshellsymbolsremovequotes(@install_path) collate database_default + '\install\replmerg.sql"' + ' -o' + '"' + fn_escapecmdshellsymbolsremovequotes(@install_path) collate database_default + '\install\replmerg.out"' IF (@osql_for_nt = 1) SELECT @osql_cmd_full = @osql_cmd_full + ' "' EXEC @retcode = master..xp_cmdshell @osql_cmd_full IF @@ERROR<> 0 OR @retcode <> 0 BEGIN RAISERROR (14113, 16, -1, @osql_cmd_full, 'replmerg.out') GOTO UNDO_INSTALL END END NO_SCRIPTS: UPDATE MSreplication_options SET value = @optbit WHERE optname = @optname IF @@ERROR <> 0 BEGIN GOTO UNDO_INSTALL END END /* Uninstall replication */ ELSE BEGIN /* ** Make sure no distributor installed before dropping ** replication stored procedures */ IF EXISTS (SELECT * FROM master..sysservers WHERE srvstatus & 8 <> 0) BEGIN RAISERROR (21021, 16, -1) RETURN(1) END UPDATE MSreplication_options SET value = @optbit WHERE optname = @optname IF @@ERROR <> 0 BEGIN GOTO FAILURE END /* *********** Do not drop replication stored procs anymore. IF LOWER(@optname collate SQL_Latin1_General_CP1_CS_AS) = 'transactional' BEGIN if exists (select * from sysobjects where type = 'P ' and name = 'sp_MSdrop_repltran') begin exec @retcode = dbo.sp_MSdrop_repltran if @@ERROR = 0 and @retcode = 0 drop procedure sp_MSdrop_repltran end END IF LOWER(@optname collate SQL_Latin1_General_CP1_CS_AS) = 'merge' BEGIN if exists (select * from sysobjects where type = 'P ' and name = 'sp_MSdrop_replmerg') begin exec @retcode = dbo.sp_MSdrop_replmerg if @@ERROR = 0 and @retcode = 0 drop procedure sp_MSdrop_replmerg end END IF NOT EXISTS (SELECT * FROM MSreplication_options WHERE value = 1) BEGIN if exists (select * from sysobjects where type = 'P ' and name = 'sp_MSdrop_replcom') begin exec @retcode = dbo.sp_MSdrop_replcom if @@ERROR = 0 and @retcode = 0 drop procedure sp_MSdrop_replcom end END */ END RETURN(0) UNDO_INSTALL: /* This is needed to drop the stored procedures that were created. */ EXEC dbo.sp_replicationoption @optname = @optname, @value = 'false', @reserved = @undo_install FAILURE: RETURN(1) GO exec dbo.sp_MS_marksystemobject sp_replicationoption go grant execute on dbo.sp_replicationoption to public if exists (select * from sysobjects where name = 'sp_vupgrade_replication' and type = 'P') drop procedure sp_vupgrade_replication go raiserror('Creating procedure sp_vupgrade_replication', 0,1) GO create procedure sp_vupgrade_replication ( @login sysname = N'sa', @password sysname = N'', @ver_old int = 517, @force_remove tinyint = 0, @security_mode bit = 0 ) as begin /* * Dispatcher proc for handling schema and metadata changes during setup initiated version upgrade * for replication components. Any schema changes to replication system tables may require modifications * here to maintain upgrade path. All modifications called in these procs are within "if exists" checks * making them repeatable for debugging and to support incremental upgrades (e.g. Beta 1 to Beta 2 to RTM) * * If server is a distributor, run new instdist.sql against all distribution dbs. * * This proc gets called by setup at the end of an install over an existing version. */ set nocount on declare @dbname sysname declare @has_dbaccess bit declare @install_path nvarchar(255) declare @osql_path nvarchar(260) declare @osql_cmd nvarchar(512) declare @osql_for_nt int declare @retcode int declare @platform_nt binary declare @db_distbit int declare @ver_min int select @db_distbit = 16 select @platform_nt = 0x1 -- raiserror('sp_vupgrade_replication', 0,1) with nowait /* * obsolete check; ver check was to prevent repl upgrade from * versions prior to SQL7.0 Beta 3; check is removed by setting @ver_min = -1 */ select @ver_min= -1 -- change if later wish to support a minimum upgrade version if ( @ver_old < @ver_min ) or ( @force_remove = 1 ) exec dbo.sp_removesrvreplication else begin /* * always need to run instdist.sql to update distribution databases on a distributor * setup must restart in non-single user mode so we can shell out to run instdist.sql scripts */ if exists( select * from master..sysdatabases where category & @db_distbit = @db_distbit ) begin /* ** Get installation path -- osql client (TOOLS) path */ EXECUTE @retcode = master.dbo.sp_MSgettools_path @osql_path OUTPUT IF ( @@ERROR <> 0 ) OR ( @retcode <> 0 ) or ( @osql_path is NULL ) or ( @osql_path = '' ) BEGIN RETURN (1) END /* ** Get installation path -- instance specific (INSTALL) directory */ exec @retcode = master.dbo.sp_MSget_setup_paths @sql_path = @install_path output IF @@ERROR<> 0 OR @retcode <> 0 or @install_path is NULL or @install_path=N'' BEGIN RETURN (1) END -- Set the flag for platform if (( platform() & @platform_nt = @platform_nt )) select @osql_for_nt = 1 else select @osql_for_nt = 0 declare cur_distdb CURSOR LOCAL FAST_FORWARD for select name, has_dbaccess(name) from master..sysdatabases where category & @db_distbit = @db_distbit for read only open cur_distdb fetch cur_distdb into @dbname, @has_dbaccess while ( @@fetch_status <> -1 ) begin -- if distribution database is available upgrade it; if offline error out if ( @has_dbaccess = 1 ) begin raiserror( 21374, 0, 1, @dbname) with nowait /* * Format osql cmd line appropriate for security mode and OS to run instdist.sql against * each distribution database. Instdist.sql will recompile procs and will also do some * schema and metadata upgrade of changed replication tables. Query timeout increased to * make enough time for alter tables in instdist.sql run for upgrade to complete. */ if ( @osql_for_nt = 1 ) select @osql_cmd = N'" "' else select @osql_cmd = N' "' -- Cannot specify -S w/ -E for local execution, SID does not map (nofix) if ( @security_mode = 1 and @osql_for_nt = 1 ) begin select @osql_cmd = @osql_cmd + fn_escapecmdshellsymbolsremovequotes(@osql_path) collate database_default + '\binn\osql" -E ' if serverproperty('instancename') is not null select @osql_cmd = @osql_cmd + ' -S"' + fn_escapecmdshellsymbols(@@SERVERNAME) collate database_default + '" ' end else select @osql_cmd = @osql_cmd + fn_escapecmdshellsymbolsremovequotes(@osql_path) collate database_default + '\binn\osql" -U' + fn_escapecmdshellsymbols(isnull(@login, N'sa')) collate database_default + ' -P' + fn_escapecmdshellsymbols(isnull(@password, N'')) collate database_default + ' -S"' + fn_escapecmdshellsymbols(@@SERVERNAME) collate database_default + '" ' select @osql_cmd = @osql_cmd + ' -l30 -t120 ' select @osql_cmd = @osql_cmd + ' -b ' + ' -d' + fn_escapecmdshellsymbols(@dbname) collate database_default select @osql_cmd = @osql_cmd + ' -i' + '"' + fn_escapecmdshellsymbolsremovequotes(@install_path) collate database_default + '\install\instdist.sql"' + ' -o' + '"' + fn_escapecmdshellsymbolsremovequotes(@install_path) collate database_default + '\install\instdist.out"' if (@osql_for_nt = 1) select @osql_cmd = @osql_cmd + ' "' exec @retcode = master..xp_cmdshell @osql_cmd if @retcode <> 0 or @@error <> 0 begin raiserror (14113, 16, -1, @osql_cmd, 'instdist.out') end /* * Process schema and metadata changes for each distribution database */ select @dbname = quotename(@dbname) exec ('use '+ @dbname + ' exec dbo.sp_vupgrade_distdb') if @@error <> 0 return(1) end else begin /* Report informational message stating distribution ** database is not accessible. */ raiserror( 21378, 10, 1, @dbname) with nowait end fetch next from cur_distdb into @dbname, @has_dbaccess end close cur_distdb deallocate cur_distdb end -- Update subscription database schema exec @retcode = dbo.sp_vupgrade_subscription_databases if @retcode <> 0 or @@error <> 0 return (1) end return (0) end go exec dbo.sp_MS_marksystemobject sp_vupgrade_replication go if exists (select * from sysobjects where name = 'sp_MScopysnapshot' and type = 'P') drop procedure sp_MScopysnapshot go raiserror('Creating procedure sp_MScopysnapshot', 0,1) go CREATE PROCEDURE sp_MScopysnapshot ( @source_folder nvarchar(255), @destination_folder nvarchar(255) ) AS BEGIN SET NOCOUNT ON DECLARE @directory_exists bit DECLARE @ftporuncdir nvarchar(5) DECLARE @pubdir nvarchar(255) DECLARE @timestampdir nvarchar(255) DECLARE @bslashindex int DECLARE @bslashindex2 int DECLARE @bslashcounter int DECLARE @command nvarchar(1000) DECLARE @retcode int DECLARE @platform_nt bit IF platform() & 0x1 = 0x1 SELECT @platform_nt = 1 ELSE SELECT @platform_nt = 0 -- If @source_folder is NULL then either the snapshot has not been -- generated or it has been cleaned up IF @source_folder IS NULL OR @source_folder = N'' BEGIN RAISERROR(21289, 16, -1) RETURN (1) END -- Make sure that the @destination folder is not null IF @destination_folder IS NULL OR @destination_folder = N'' BEGIN RAISERROR(21287, 16, -1) RETURN (1) END -- Append backslash to @destination_folder if it is not -- there already IF SUBSTRING(@destination_folder, LEN(@destination_folder), 1) <> N'\' BEGIN SELECT @destination_folder = @destination_folder + N'\' END -- Check if the destination folder exists EXEC sp_MSget_file_existence @destination_folder, @directory_exists OUTPUT IF @directory_exists = 0 BEGIN RAISERROR(21287, 16, -1) RETURN (1) END -- Parse out the last three components in the source folder -- Note that the source_folder must have a trailing backslash SELECT @bslashindex = 1 SELECT @bslashindex2 = 1 SELECT @bslashcounter = 0 WHILE (@bslashindex <> 0) BEGIN SELECT @bslashindex = CHARINDEX(N'\', @source_folder, @bslashindex + 1) SELECT @bslashcounter = @bslashcounter + 1 IF @bslashcounter > 4 BEGIN SELECT @bslashindex2 = CHARINDEX(N'\', @source_folder, @bslashindex2 + 1) END END SELECT @bslashindex = CHARINDEX(N'\', @source_folder, @bslashindex2 + 1) SELECT @ftporuncdir = SUBSTRING(@source_folder, @bslashindex2 + 1, @bslashindex - @bslashindex2 - 1) SELECT @bslashindex2 = @bslashindex SELECT @bslashindex = CHARINDEX(N'\', @source_folder, @bslashindex2 + 1) SELECT @pubdir = SUBSTRING(@source_folder, @bslashindex2 + 1, @bslashindex - @bslashindex2 - 1) SELECT @bslashindex2 = @bslashindex SELECT @bslashindex = CHARINDEX(N'\', @source_folder, @bslashindex2 + 1) SELECT @timestampdir = SUBSTRING(@source_folder, @bslashindex2 + 1, @bslashindex - @bslashindex2 - 1) SELECT @bslashindex2 = @bslashindex -- Create the subdirectory structure underneath the specified snapshot -- folder. Ignore errors for now, we will check whether the directory -- is successfully created later on. -- Don't suppress output from xp_cmdshell so user knows what's going on -- in case something goes wrong SELECT @destination_folder = @destination_folder + @ftporuncdir + '\' SELECT @command = 'mkdir "' + fn_escapecmdshellsymbolsremovequotes(@destination_folder) collate database_default + '"' IF (@platform_nt = 1) SELECT @command = '" ' + @command + ' "' EXEC master..xp_cmdshell @command SELECT @destination_folder = @destination_folder + @pubdir + '\' SELECT @command = 'mkdir "' + fn_escapecmdshellsymbolsremovequotes(@destination_folder) collate database_default + '"' IF (@platform_nt = 1) SELECT @command = '" ' + @command + ' "' EXEC master..xp_cmdshell @command SELECT @destination_folder = @destination_folder + @timestampdir + '\' SELECT @command = 'mkdir "' + fn_escapecmdshellsymbolsremovequotes(@destination_folder) collate database_default + '"' IF (@platform_nt = 1) SELECT @command = '" ' + @command + ' "' EXEC master..xp_cmdshell @command -- Check if the real destination folder exists EXEC sp_MSget_file_existence @destination_folder, @directory_exists OUTPUT IF @directory_exists = 0 BEGIN RAISERROR(21288, 16, -1) RETURN (1) END -- Do the actual copying SELECT @command = 'copy "' + fn_escapecmdshellsymbolsremovequotes(@source_folder) collate database_default + '*.*" "' + fn_escapecmdshellsymbolsremovequotes(@destination_folder) collate database_default + '"' IF (@platform_nt = 1) SELECT @command = '" ' + @command + ' "' EXEC @retcode = master..xp_cmdshell @command IF @retcode <> 0 RETURN (1) ELSE RETURN (0) END go EXEC dbo.sp_MS_marksystemobject sp_MScopysnapshot go if exists (select * from sysobjects where name = 'sp_copymergesnapshot' and type = 'P') drop procedure sp_copymergesnapshot go raiserror('Creating procedure sp_copymergesnapshot', 0,1) go CREATE PROCEDURE sp_copymergesnapshot ( @publication sysname, @destination_folder nvarchar(255) ) AS BEGIN SET NOCOUNT ON DECLARE @retcode int DECLARE @source_folder nvarchar(255) SELECT @retcode = 0 EXEC @retcode = dbo.sp_MSreplcheck_publish IF @@ERROR <> 0 OR @retcode <> 0 RETURN (1) CREATE TABLE #snapshot_folders ( id int identity, snapshot_folder nvarchar(255) collate database_default ) IF @@ERROR <> 0 BEGIN RETURN 1 END INSERT INTO #snapshot_folders EXEC @retcode = sp_browsemergesnapshotfolder @publication = @publication IF @retcode <> 0 OR @@ERROR <> 0 BEGIN GOTO Failure END SELECT @source_folder = (select top 1 snapshot_folder FROM #snapshot_folders ORDER BY id ASC) IF @@ERROR <> 0 BEGIN GOTO Failure END SET ROWCOUNT 0 EXEC @retcode = sp_MScopysnapshot @source_folder, @destination_folder IF @retcode <> 0 OR @@ERROR <> 0 BEGIN GOTO Failure END DROP TABLE #snapshot_folders RETURN 0 Failure: SET ROWCOUNT 0 DROP TABLE #snapshot_folders RETURN 1 END GO EXEC dbo.sp_MS_marksystemobject sp_copymergesnapshot go grant execute on dbo.sp_copymergesnapshot to public go if exists (select * from sysobjects where name = 'sp_copysnapshot' and type = 'P') drop procedure sp_copysnapshot go raiserror('Creating procedure sp_copysnapshot', 0,1) go CREATE PROCEDURE sp_copysnapshot ( @publication sysname, @destination_folder nvarchar(255), @subscriber sysname = NULL, @subscriber_db sysname = NULL ) AS BEGIN SET NOCOUNT ON DECLARE @retcode int DECLARE @source_folder nvarchar(255) SELECT @retcode = 0 EXEC @retcode = dbo.sp_MSreplcheck_publish IF @@ERROR <> 0 OR @retcode <> 0 RETURN (1) CREATE TABLE #snapshot_folders ( id int identity, snapshot_folder nvarchar(255) collate database_default ) IF @@ERROR <> 0 BEGIN RETURN 1 END INSERT INTO #snapshot_folders EXEC @retcode = sp_browsesnapshotfolder @publication = @publication, @subscriber = @subscriber, @subscriber_db = @subscriber_db IF @retcode <> 0 OR @@ERROR <> 0 BEGIN GOTO Failure END SELECT @source_folder = (select top 1 snapshot_folder FROM #snapshot_folders ORDER BY id) IF @@ERROR <> 0 BEGIN GOTO Failure END SET ROWCOUNT 0 EXEC @retcode = sp_MScopysnapshot @source_folder, @destination_folder IF @retcode <> 0 OR @@ERROR <> 0 BEGIN GOTO Failure END DROP TABLE #snapshot_folders RETURN 0 Failure: SET ROWCOUNT 0 DROP TABLE #snapshot_folders RETURN 1 END GO EXEC dbo.sp_MS_marksystemobject sp_copysnapshot go grant execute on dbo.sp_copysnapshot to public go if exists (select * from sysobjects where type = 'P ' and name = 'sp_copysubscription') drop procedure sp_copysubscription go print '' print 'Creating procedure sp_copysubscription' go CREATE PROCEDURE sp_copysubscription ( @filename nvarchar(260), @temp_dir nvarchar(260) = NULL, -- Directory contains temp files. If not specified, SQL -- server default data directory will be used. @overwrite_existing_file bit = 0 ) AS SET NOCOUNT ON /* ** Declarations. */ declare @cmd nvarchar(4000) declare @retcode int declare @data_path nvarchar(260) declare @subscriber_srvid int declare @subscriber_db sysname declare @backup_path nvarchar(260) declare @temp_data_path nvarchar(260) declare @temp_log_path nvarchar(260) declare @retention int declare @retention_date datetime declare @pubid uniqueidentifier /* * Initializations */ select @retcode = 0 select @subscriber_srvid = 0 select @subscriber_db = db_name() -- We only support single file attach. Check to make sure -- there are only 2 files, one data file and one log file if (select count(*) from sysfiles) > 2 begin raiserror(21212,16, -1) return 1 end /* Make sure all tran sub allows attach */ declare @publication sysname declare @publisher sysname declare @tran_found bit declare @merge_found bit select @tran_found = 0 select @merge_found = 0 /* ** Make sure all merge subscriptions in the current database ** have allow_subscription_copy set to TRUE ** and there are no push subscriptions. */ if exists (select * from sysobjects where name = 'MSsubscription_agents') begin set @publisher = NULL -- Not using @publication because share agent case. select top 1 @publisher = publisher from MSsubscription_agents where allow_subscription_copy = 0 IF @publisher is not null BEGIN RAISERROR(21236, 16, -1, @publisher) RETURN (1) END set @publisher = null select top 1 @publisher = publisher from MSreplication_subscriptions where subscription_type = 0 IF @publisher is not null BEGIN RAISERROR(21237, 16, -1, @publisher) RETURN (1) END if exists (select * from MSsubscription_agents) select @tran_found = 1 end /* ** Make sure all merge subscriptions in the current database ** have allow_subscription_copy set to TRUE ** and there are no push subscriptions. */ if exists (select * from sysobjects where name = 'sysmergepublications') begin set @publication = NULL select top 1 @publication = name from sysmergepublications where allow_subscription_copy = 0 IF @publication is not null BEGIN RAISERROR (21204, 16, -1, @publication) RETURN (1) END set @publication = null select top 1 @publication = p.name from sysmergepublications p, sysmergesubscriptions s where p.pubid = s.pubid and s.subid <> s.pubid and db_name = db_name() collate database_default and subscriber_server = convert(nvarchar(4000), SERVERPROPERTY('ServerName')) collate database_default and s.subscription_type = 0 IF @publication is not null BEGIN RAISERROR(21238, 16, -1, @publication) RETURN (1) END -- Does db contains subscriptions? if exists (select * from sysmergesubscriptions where subid <> pubid and db_name = db_name() collate database_default and subscriber_server = convert(nvarchar(4000), SERVERPROPERTY('ServerName')) collate database_default) select @merge_found = 1 /* Retention check : Make sure that the subscription copy is not too old */ declare PC CURSOR LOCAL FAST_FORWARD for select DISTINCT p.name, p.pubid, p.retention from sysmergepublications p, sysmergesubscriptions s where s.subid=p.pubid and s.pubid=p.pubid for read only open PC fetch PC into @publication, @pubid, @retention WHILE (@@fetch_status <> -1) BEGIN /* Compute the retention period cutoff dates per publication */ select @retention_date = dateadd(day, -@retention, getdate()) if @retention is not NULL and @retention > 0 begin if not exists (select coldate from sysmergearticles , MSmerge_genhistory where nickname = art_nick and coldate > @retention_date and sysmergearticles.pubid = @pubid) begin RAISERROR (21306, 16, -1, @publication) return (1) end end fetch PC into @publication, @pubid, @retention END end if @tran_found = 0 and @merge_found = 0 begin raiserror(21239, 16 , -1) return (1) end -- Security check -- Only DBO or sysadmin can do this -- The user also must have create db permissions. exec @retcode = dbo.sp_MSreplcheck_subscribe IF @retcode <> 0 or @@error <> 0 return 1 if @overwrite_existing_file is null set @overwrite_existing_file = 0 -- Check to see if the file already exists declare @exists bit if @overwrite_existing_file = 0 begin exec @retcode = dbo.sp_MSget_file_existence @filename, @exists output if @@error <> 0 or @retcode <> 0 return 1 if @exists <> 0 begin raiserror(21214, 16, -1, @filename) return 1 end end -- Check to see if have write permissions to the file location. -- Try create the file -- Echo text can be anything. select @cmd = 'echo Subscription copy failed. > "' + fn_escapecmdshellsymbolsremovequotes(@filename) collate database_default + '"' exec @retcode = master.dbo.xp_cmdshell @cmd, NO_OUTPUT if @@error <> 0 or @retcode <> 0 begin raiserror(21247, 16, -1, @filename) select @retcode = 1 goto Cleanup end -- File should be created. exec @retcode = dbo.sp_MSget_file_existence @filename, @exists output if @@error <> 0 or @retcode <> 0 begin select @retcode = 1 goto Cleanup end if @exists = 0 begin raiserror(21247,16, -1, @filename) select @retcode = 1 goto Cleanup end /* ** Get the MSSQL DATA path. Note that users can have a SQLDataRoot directory different from SQLPath */ if @temp_dir is null begin exec @retcode = master.dbo.sp_MSget_setup_paths @data_path = @temp_dir output IF @retcode <> 0 or @@error <> 0 return 1 select @temp_dir = @temp_dir + '\DATA\' end else begin -- Check to make sure working dir is valid. exec @retcode = dbo.sp_MSget_file_existence @temp_dir, @exists output if @@error <> 0 or @retcode <> 0 begin select @retcode = 1 goto Cleanup end if @exists = 0 begin raiserror (21037, 16, -1, @temp_dir) select @retcode = 1 goto Cleanup end if substring(@temp_dir, len(@temp_dir), 1) <> '\' select @temp_dir = @temp_dir + '\' end -- Get temp db name -- Use a guid to avoid name colision. declare @dbname sysname select @dbname = db_name() declare @temp_db_name sysname declare @guid_name nvarchar(36) select @guid_name = convert (nvarchar(36), newid()) select @temp_db_name = 'repl_sub_restore_' + @guid_name select @backup_path = @temp_dir + @temp_db_name + '.bak' -- Create table used to signal attach or restored process to do different things if not exists (select * from sysobjects where name = 'MSreplication_restore_stage') begin CREATE TABLE dbo.MSreplication_restore_stage ( stage_id int -- not used for now ) IF @@ERROR <> 0 return 1 end -- First backup the database to the file given -- Overwrite the existing file with INIT option. BACKUP DATABASE @dbname TO DISK = @backup_path WITH INIT if @@error<> 0 begin select @retcode = 1 goto Cleanup end -- Restore it to a temporary working database -- Get phy data and log file name for the temp db select @temp_data_path = @temp_dir + @temp_db_name + '.mdf' select @temp_log_path = @temp_dir + @temp_db_name + '.ldf' -- Get the command select @cmd = 'restore database ' + quotename(@temp_db_name) + ' from disk = ' + quotename(@backup_path,'''') + ' with replace, move ' -- Get the logical file name for data file. select @cmd = @cmd + quotename(rtrim(name),'''') from sysfiles where (status & 0x40) = 0 -- Use passed in filename as phy data file name for the temp db -- Use the passed in file as phy data file for the temp db select @cmd = @cmd + ' to ' + quotename(@temp_data_path,'''') + ', move ' -- Get the logical file name for the log file select @cmd = @cmd + quotename(rtrim(name),'''') from sysfiles where (status & 0x40) <> 0 -- Use the passed in file as phy file for the temp db select @cmd = @cmd + ' to ' + quotename(@temp_log_path,'''') + ' ' exec (@cmd) if @@error<> 0 begin select @retcode = 1 goto Cleanup end -- Once we successfully restored, we delete to back up file to save disk space. if @backup_path is not null begin select @cmd = 'del "' + fn_escapecmdshellsymbolsremovequotes(@backup_path) collate database_default + '"' EXEC master..xp_cmdshell @cmd, NO_OUTPUT set @backup_path = null end -- Prepare the database for detach. 2 things will be done -- 1. Set a flag to indicate that this is a prepare sub db for detach -- 2. For merge, create table to store sysservers info for later fixing up after attach select @cmd = quotename(@temp_db_name) + '.dbo.sp_MSprepare_sub_for_detach' exec @retcode = @cmd @subscriber_srvid = @subscriber_srvid, @subscriber_db = @subscriber_db if @retcode<>0 or @@error<>0 begin select @retcode = 1 goto Cleanup end -- Shink the size of the temp db before detach DBCC SHRINKDATABASE (@temp_db_name, 10) if @@error <> 0 goto Cleanup -- detach the database -- Wait for the db to be closed WAITFOR DELAY '00:00:00.500' exec @retcode = sp_detach_db @temp_db_name if @retcode<>0 or @@error<>0 begin select @retcode = 1 goto Cleanup end -- Delete the log file to save disk space if @temp_log_path is not null begin select @cmd = 'del "' + fn_escapecmdshellsymbolsremovequotes(@temp_log_path) collate database_default + '"' EXEC master..xp_cmdshell @cmd, NO_OUTPUT set @temp_log_path = null end -- Compress the file exec @retcode = master..xp_makecab @cabfilename = @filename, @compression_mode ='mszip', @verbose_level = 0, @filename1 = @temp_data_path if @retcode<>0 or @@error<>0 begin select @retcode = 1 goto Cleanup end Cleanup: if exists (select * from sysobjects where name = 'MSreplication_restore_stage') drop table dbo.MSreplication_restore_stage if exists (select * from master..sysdatabases where name = @temp_db_name collate database_default) begin select @cmd = 'drop database ' + quotename(@temp_db_name) exec (@cmd) end if @backup_path is not null begin select @cmd = 'del "' + fn_escapecmdshellsymbolsremovequotes(@backup_path) collate database_default + '"' EXEC master..xp_cmdshell @cmd, NO_OUTPUT end if @temp_data_path is not null begin select @cmd = 'del "' + fn_escapecmdshellsymbolsremovequotes(@temp_data_path) collate database_default + '"' EXEC master..xp_cmdshell @cmd, NO_OUTPUT end if @temp_log_path is not null begin select @cmd = 'del "' + fn_escapecmdshellsymbolsremovequotes(@temp_log_path) collate database_default + '"' EXEC master..xp_cmdshell @cmd, NO_OUTPUT end if @retcode <> 0 begin select @cmd = 'del "' + fn_escapecmdshellsymbolsremovequotes(@filename) collate database_default + '"' EXEC master..xp_cmdshell @cmd, NO_OUTPUT end return @retcode go EXEC dbo.sp_MS_marksystemobject sp_copysubscription GO grant execute on dbo.sp_copysubscription to public if exists (select * from sysobjects where type = 'P ' and name = 'sp_attachsubscription') drop procedure sp_attachsubscription go print '' print 'Creating procedure sp_attachsubscription' go CREATE PROCEDURE sp_attachsubscription ( @dbname sysname, @filename nvarchar(260), @subscriber_security_mode int = NULL, /* 0 standard; 1 integrated */ @subscriber_login sysname = NULL, @subscriber_password sysname = NULL ) AS SET NOCOUNT ON /* ** Declarations. */ declare @cmd nvarchar(4000) declare @retcode int DECLARE @platform_nt binary declare @copy_created bit declare @exists bit select @platform_nt = 0x1 select @retcode = 0 select @copy_created = 0 select @exists = 0 -- Parameter check: @subscriber_security_mode if @subscriber_security_mode is null begin if ( platform() & @platform_nt ) = @platform_nt select @subscriber_security_mode = 1 else select @subscriber_security_mode = 0 end if ( ( platform() & @platform_nt ) <> @platform_nt and @subscriber_security_mode = 1 ) begin RAISERROR(21038, 16, -1) RETURN (1) end if (@subscriber_security_mode = 0) and (@subscriber_login IS NULL or rtrim(@subscriber_login) = '') set @subscriber_login = 'sa' -- Check to make sure the database does not exists. if exists (select * from master..sysdatabases where name = @dbname collate database_default) begin raiserror(20621, 16, -1, @dbname) return (1) end -- Check to see if users has permissions to create database -- permissions() have to be run in master to return create db permission. declare @pm int exec @retcode = master.dbo.sp_executesql N'select @pm = permissions()', N'@pm int output', @pm output if @@error <> 0 or @retcode <> 0 return 1 if @pm & 1 = 0 begin raiserror(20618, 16, -1) return 1 end -- Decompress the file -- We have to copy the file to another location first. -- (cannot use source as destination') declare @temp_copy nvarchar(260) declare @file_dir nvarchar(260) declare @file_name nvarchar(260) declare @dir_cmd nvarchar(260) -- Set @drive_cmd if needed -- Set temp copy to be the file directory first -- Note @file_dir include '\' if (charindex('\', @filename, 1) = 0) begin select @file_dir = '' select @file_name = @filename end else begin select @file_dir = left(@filename,len(@filename) + 1 - charindex('\', reverse(@filename), 1)) select @file_name = right(@filename, len(@filename) - len(@file_dir)) end -- Get guid name declare @guid_name nvarchar(36) select @guid_name = convert (nvarchar(36), newid()) select @temp_copy = @file_dir + @guid_name + '.tmp' -- copy file select @cmd = 'copy "' + fn_escapecmdshellsymbolsremovequotes(@filename) collate database_default + '" "' + fn_escapecmdshellsymbolsremovequotes(@temp_copy) collate database_default + '"' exec @retcode = master.dbo.xp_cmdshell @cmd, NO_OUTPUT if @@error <> 0 or @retcode <> 0 begin raiserror(21248, 16, -1, @filename) select @retcode = 1 goto Cleanup end exec @retcode = dbo.sp_MSget_file_existence @temp_copy, @exists output if @@error <> 0 or @retcode <> 0 begin select @retcode = 1 goto Cleanup end if @exists = 0 begin raiserror(21247, 16, -1, @temp_copy) select @retcode = 1 goto Cleanup end select @copy_created = 1 -- decompress exec @retcode = master.dbo.xp_unpackcab @cabfilename = @temp_copy, @destination_folder = @file_dir, @verbose_level = 0, @destination_file = @file_name, @suppress_messages = 1 if @@error <> 0 begin select @retcode = 1 goto Cleanup end if @retcode in (1030,1029,2005) begin raiserror(20609, 16, -1, @filename) select @retcode = 1 goto Cleanup end else if @retcode <> 0 -- re-issue the command to get errors begin exec @retcode = master.dbo.xp_unpackcab @cabfilename = @temp_copy, @destination_folder = @file_dir, @verbose_level = 0, @destination_file = @file_name, @suppress_messages = 0 select @retcode = 1 goto Cleanup end -- Attach exec @retcode = dbo.sp_attach_single_file_db @dbname = @dbname, @physname = @filename if @retcode<>0 or @@error<>0 begin raiserror(21248, 16, -1, @filename) select @retcode = 1 goto Cleanup end -- Prepare the database for detach. 2 things will be done -- 1. Set a flag to indicate that this is a prepare sub db for detach -- 2. For merge, create table to store sysservers info for later fixing up after attach select @cmd = quotename(@dbname) + '.dbo.sp_MSrestore_sub' exec @retcode = @cmd @subscriber_security_mode = @subscriber_security_mode, @subscriber_login = @subscriber_login, @subscriber_password = @subscriber_password if @retcode<>0 or @@error<>0 begin select @retcode = 1 goto Cleanup end Cleanup: if @retcode <> 0 begin -- The files will be deleted if some thing failed. if exists (select * from master..sysdatabases where name = @dbname collate database_default) begin select @cmd = 'drop database ' + quotename(@dbname) exec (@cmd) end end if @temp_copy is not null begin -- Restore the original file, ignore errors if @retcode <> 0 and @copy_created = 1 begin select @cmd = 'copy "' + fn_escapecmdshellsymbolsremovequotes(@temp_copy) collate database_default + '" "' + fn_escapecmdshellsymbolsremovequotes(@filename) collate database_default + '"' exec master.dbo.xp_cmdshell @cmd, NO_OUTPUT end -- Delete the temp file. select @cmd = 'del "' + fn_escapecmdshellsymbolsremovequotes(@temp_copy) collate database_default+ '"' EXEC master..xp_cmdshell @cmd, NO_OUTPUT end return @retcode go EXEC dbo.sp_MS_marksystemobject sp_attachsubscription grant execute on dbo.sp_attachsubscription to public GO -------------------------------------------------------------------------------- --. System objects (replcom.sql) -------------------------------------------------------------------------------- if exists (select * from sysobjects where type = 'P' and name = 'sp_adddistributiondb') drop procedure sp_adddistributiondb GO raiserror('Creating procedure sp_adddistributiondb', 0,1) go CREATE PROCEDURE sp_adddistributiondb ( @database sysname, @data_folder nvarchar(255) = NULL, @data_file nvarchar(255) = NULL, /* physical file name */ @data_file_size int = 2, /* Default: 2MB */ @log_folder nvarchar(255) = NULL, @log_file nvarchar(255) = NULL, /* physical file name */ @log_file_size int = 0, @min_distretention int = 0, /* min distribution retention period in hours */ @max_distretention int = 72, /* max distribution retention period in hours */ @history_retention int = 48, /* history retention period in hours */ @security_mode int = 0, /* distributor login security 0 standard 1 integrated */ @login sysname = 'sa', /* standard login name */ @password sysname = NULL, /* standard login password */ @createmode int = 0, /* 0: use create db for attach (recommended), 1: create db or use existing but no attach (this is the old way), 2: create for instdist and detach only */ @from_scripting bit = 0 ) AS SET NOCOUNT ON /* ** Declarations. */ DECLARE @data_path nvarchar(512) DECLARE @log_path nvarchar(512) DECLARE @data_path_quoted_for_copy nvarchar(512) DECLARE @log_path_quoted_for_copy nvarchar(512) DECLARE @logical_data_file nvarchar(255) DECLARE @logical_log_file nvarchar(255) DECLARE @canneddbdata_file nvarchar(255) DECLARE @canneddblog_file nvarchar(255) DECLARE @filecopy_cmd nvarchar(255) DECLARE @file_exists bit DECLARE @data_file_preexists int DECLARE @log_file_preexists int DECLARE @osql_path nvarchar(260) DECLARE @osql_cmd nvarchar(1000) DECLARE @osql_for_nt int DECLARE @devnum int --DECLARE @num_pages int DECLARE @retcode int DECLARE @reg_key nvarchar(255) DECLARE @agentname nvarchar(100) DECLARE @command nvarchar (2048) DECLARE @distbit int DECLARE @install_path nvarchar(255) DECLARE @mssql_data_path nvarchar(255) DECLARE @on_clause nvarchar(512) DECLARE @logon_clause nvarchar(512) DECLARE @distproc nvarchar(255) DECLARE @major_version int DECLARE @db_exists bit DECLARE @trunc_log_bit int DECLARE @description nvarchar(100) DECLARE @category_name sysname DECLARE @createmode_attach int DECLARE @createmode_noattach int DECLARE @createmode_fordetach int --DECLARE @filegrowth nvarchar(10) DECLARE @data_file_size_str nvarchar(10) DECLARE @log_file_size_str nvarchar(10) DECLARE @platform_nt binary --DECLARE @max_datafile_size int --DECLARE @max_logfile_size int IF @password = N'' select @password = NULL select @platform_nt = 0x1 --select @filegrowth = N'512KB' -- on error, delete the data and log files only if they didn't pre-exist. -- by default, assume they pre-exist. select @data_file_preexists = 1 select @log_file_preexists = 1 select @file_exists = 0 if (@data_file_size IS NULL) or (@data_file_size = 0) select @data_file_size_str = N'512KB' else select @data_file_size_str = convert(nvarchar(10), @data_file_size) if (@log_file_size IS NULL) or (@log_file_size = 0) select @log_file_size_str = N'512KB' else select @log_file_size_str = convert(nvarchar(10), @log_file_size) --if (@data_file_size > 16) -- select @max_datafile_size = @data_file_size --else -- select @max_datafile_size = 16 --if (@log_file_size > 16) -- select @max_logfile_size = @log_file_size --else -- select @max_logfile_size = 16 select @createmode_attach = 0, @createmode_noattach = 1, @createmode_fordetach = 2 SELECT @trunc_log_bit = 8 SELECT @distbit = 16 if (@createmode <> @createmode_fordetach) begin /* ** Check if replication components are installed on this server */ exec @retcode = dbo.sp_MS_replication_installed if (@retcode <> 1) begin return (1) end /* ** Check for invalid security modes */ IF @security_mode < 0 OR @security_mode > 1 BEGIN RAISERROR(14109, 16, -1) RETURN (1) END IF ( ( @platform_nt != platform() & @platform_nt ) and @security_mode = 1) BEGIN RAISERROR(21038, 16, -1) RETURN (1) END /* ** Check for invalid retention values */ IF @min_distretention < 0 OR @max_distretention < 0 BEGIN RAISERROR(14106, 16, -1) RETURN (1) END IF @min_distretention > @max_distretention BEGIN RAISERROR(14107, 16, -1) RETURN (1) END /* ** Check to make sure this is a distributor */ IF NOT EXISTS (SELECT * FROM master..sysservers WHERE UPPER(datasource) = UPPER(@@SERVERNAME) collate database_default AND srvstatus & 8 <> 0) BEGIN RAISERROR (14114, 16, -1, @@SERVERNAME) RETURN(1) END /* ** Check if database is already configured as a distributor database */ IF EXISTS (SELECT * FROM msdb..MSdistributiondbs WHERE name = @database collate database_default) BEGIN RAISERROR (14119, 16, -1, @database) RETURN(1) END end /* ** Get path to osql client (TOOLS) directory */ EXECUTE @retcode = master.dbo.sp_MSgettools_path @osql_path OUTPUT IF ( @retcode <> 0 ) or ( @@ERROR <> 0 ) or ( @osql_path is NULL ) or ( @osql_path = '' ) BEGIN GOTO UNDO END /* ** Get path to version specific INSTALL directory */ exec @retcode = master.dbo.sp_MSget_setup_paths @sql_path = @install_path output, @data_path = @mssql_data_path output IF @retcode <> 0 or @install_path is NULL or @install_path='' or @mssql_data_path = '' BEGIN GOTO UNDO END IF @data_folder IS NULL or @data_folder = '' select @data_folder = @mssql_data_path + '\DATA' IF @log_folder IS NULL or @log_folder = '' select @log_folder = @mssql_data_path + '\DATA' IF @data_file IS NULL SELECT @data_file = @database + '.MDF' IF @log_file IS NULL SELECT @log_file = @database + '.LDF' if substring(@data_folder, len(@data_folder), 1) = '\' select @data_folder = substring (@data_folder, 1, len(@data_folder) -1) if substring(@log_folder, len(@log_folder), 1) = '\' select @log_folder = substring (@log_folder, 1, len(@log_folder) -1) SELECT @data_path = @data_folder + '\' + @data_file SELECT @log_path = @log_folder + '\' + @log_file SELECT @data_path_quoted_for_copy = '"' + fn_escapecmdshellsymbolsremovequotes(@data_folder) collate database_default + '\' + fn_escapecmdshellsymbolsremovequotes(@data_file) collate database_default + '"' SELECT @log_path_quoted_for_copy = '"' + fn_escapecmdshellsymbolsremovequotes(@log_folder) collate database_default + '\' + fn_escapecmdshellsymbolsremovequotes(@log_file) collate database_default + '"' select @logical_data_file = @database /* ** Truncate the logical log file name back to 128 characters ** long so the 'CREATE DATABASE' statement won't complain. */ /* LEN(@logical_log_file) = LEN(@database) + LEN('_log') and LEN(@logical_log_file) <= 128 implies LEN(@database) <=124 */ IF (LEN(@database) > 124) SELECT @logical_log_file = SUBSTRING(@database, 1, 124) + '_log' ELSE SELECT @logical_log_file = @database + '_log' if (@createmode = @createmode_attach) begin select @canneddbdata_file = @mssql_data_path + '\DATA\DISTMDL.MDF' select @canneddblog_file = @mssql_data_path + '\DATA\DISTMDL.LDF' exec dbo.sp_MSget_file_existence @canneddbdata_file, @file_exists OUTPUT if (@file_exists = 0) begin /* Fallback to mode where instdist.sql needs to be run */ select @createmode = @createmode_noattach end exec dbo.sp_MSget_file_existence @canneddblog_file, @file_exists OUTPUT if (@file_exists = 0) begin /* Fallback to mode where instdist.sql needs to be run */ select @createmode = @createmode_noattach end end /* ** Create the distributor database if it does not exist */ IF NOT EXISTS (SELECT * from master..sysdatabases WHERE name = @database collate database_default) AND (@createmode <> @createmode_attach) BEGIN -- Note: Use system's default file growth. IF @logical_data_file IS NOT NULL AND NOT EXISTS (SELECT * FROM master..sysdevices WHERE name = @logical_data_file collate database_default) BEGIN SELECT @on_clause = ' ON (NAME =''' + @logical_data_file + ''',FILENAME=''' + REPLACE( @data_path, '''', '''''' ) + ''', SIZE=' + @data_file_size_str + ', MAXSIZE = UNLIMITED)' END IF @logical_log_file IS NOT NULL AND NOT EXISTS (SELECT * FROM master..sysdevices WHERE name = @logical_log_file collate database_default) BEGIN SELECT @logon_clause = ' LOG ON (NAME =''' + @logical_log_file + ''',FILENAME=''' + REPLACE( @log_path, '''', '''''' ) + ''', SIZE=' + @log_file_size_str + ', MAXSIZE= UNLIMITED)' END /* ** Create distributor database */ SELECT @command = 'USE master CREATE DATABASE ' + QUOTENAME(@database) + + isnull(@on_clause, ' ') + isnull(@logon_clause, ' ') EXEC (@command) IF @@ERROR <> 0 RETURN (1) SELECT @db_exists = 0 END ELSE IF NOT EXISTS (SELECT * from master..sysdatabases WHERE name = @database collate database_default) AND (@createmode = @createmode_attach) BEGIN /* DO THE CREATE DATABASE FOR ATTACH STUFF */ exec dbo.sp_MSget_file_existence @data_path, @data_file_preexists OUTPUT if (@data_file_preexists = 1) begin raiserror(5170, 16, -1, @data_path) return 1 end SELECT @on_clause = ' ON (NAME = ''' + @logical_data_file + ''', FILENAME=''' + REPLACE( @data_path, '''', '''''' ) + ''')' exec dbo.sp_MSget_file_existence @log_path, @log_file_preexists OUTPUT if (@log_file_preexists = 1) begin raiserror(5170, 16, -1, @log_path) return 1 end SELECT @logon_clause = ' LOG ON (NAME = ''' + @logical_log_file + ''', FILENAME=''' + REPLACE( @log_path, '''', '''''' ) + ''')' select @filecopy_cmd = 'copy "' + fn_escapecmdshellsymbolsremovequotes(@canneddbdata_file) collate database_default + '" ' + @data_path_quoted_for_copy EXEC @retcode = master..xp_cmdshell @filecopy_cmd, NO_OUTPUT IF @retcode <> 0 OR @@ERROR <> 0 BEGIN RAISERROR (14113, 16, -1, @filecopy_cmd, 'instdist.out') return (1) END select @filecopy_cmd = 'copy "' + fn_escapecmdshellsymbolsremovequotes(@canneddblog_file) collate database_default + '" ' + @log_path_quoted_for_copy EXEC @retcode = master..xp_cmdshell @filecopy_cmd, NO_OUTPUT IF @retcode <> 0 OR @@ERROR <> 0 BEGIN RAISERROR (14113, 16, -1, @filecopy_cmd, 'instdist.out') return (1) END /* ** Create distributor database */ SELECT @command = 'USE master CREATE DATABASE ' + QUOTENAME(@database) + + @on_clause + @logon_clause + ' FOR ATTACH' EXEC (@command) IF @@ERROR <> 0 begin RETURN (1) end dbcc dbreindexall(@database, 240) with no_infomsgs SELECT @db_exists = 0 END ELSE BEGIN SELECT @db_exists = 1 END -- Must make the dist db owned by sa so that the sps in it can select from -- security cache tables in tempdb by owership chain rule. declare @retcode2 int select @retcode2 = 0 select @distproc = QUOTENAME(@database) + '.dbo.sp_executesql' SELECT @command = -- If the db is created by sa or from attach, sa is dbo already. -- sp_changedbowner will fail is the new owner is an user in the db already. ' if not exists (select * from sysusers where sid = 0x01) ' + ' exec @retcode2 = dbo.sp_changedbowner ''sa''' EXEC @retcode = @distproc @command, N'@retcode2 int output', @retcode2 output IF @retcode <> 0 or @retcode2 <> 0 or @@ERROR <> 0 BEGIN GOTO UNDO END /* Set the database option truncate log on checkpoint & turn off autoclose which is default of win9x*/ IF EXISTS (SELECT * FROM master.dbo.sysdatabases WHERE name = @database collate database_default AND (status & @trunc_log_bit) = 0 ) -- if its not already marked BEGIN EXEC @retcode = dbo.sp_dboption @database, 'trunc. log on chkpt.', 'true' IF @retcode <> 0 OR @@ERROR <> 0 BEGIN GOTO UNDO END END EXEC @retcode = dbo.sp_dboption @database, 'autoclose', 'false' IF @retcode <> 0 OR @@ERROR <> 0 BEGIN GOTO UNDO END /* ** ** Update sysdatabase category bit ** This is to prevent user from dropping the database. **/ if (@createmode <> @createmode_fordetach) begin UPDATE master..sysdatabases SET category = category | @distbit WHERE name = @database collate database_default IF @@ERROR <> 0 BEGIN GOTO UNDO END end /* ** Install instdist.sql */ if (@createmode <> @createmode_attach) OR (@db_exists = 1) begin if (( platform() & @platform_nt = @platform_nt )) select @osql_for_nt = 1 else select @osql_for_nt = 0 -- Always use integrated security on WINNT since @login passed-in is for remote -- subscriber and may not have enough privilege to apply the script IF (@security_mode = 1 or @osql_for_nt = 1) AND NOT (@security_mode = 0 AND @createmode = 2) BEGIN SELECT @osql_cmd = '" "' + fn_escapecmdshellsymbolsremovequotes(@osql_path) collate database_default + '\binn\osql" -E ' if serverproperty('instancename') is not null SELECT @osql_cmd = @osql_cmd + ' -S"' + fn_escapecmdshellsymbols(@@SERVERNAME) collate database_default + '" ' END ELSE BEGIN -- cannot specify -S w/ -E for local execution, SID does not map if (@osql_for_nt = 1) SELECT @osql_cmd = '" "' + fn_escapecmdshellsymbolsremovequotes(@osql_path) collate database_default + '\binn\osql" -U"' + fn_escapecmdshellsymbols(@login) collate database_default + '" -P"' + fn_escapecmdshellsymbols(isnull(@password,'')) collate database_default + '" -S"' + fn_escapecmdshellsymbols(@@SERVERNAME) collate database_default + '" ' else SELECT @osql_cmd = '"' + fn_escapecmdshellsymbolsremovequotes(@osql_path) collate database_default + '\binn\osql" -U"' + fn_escapecmdshellsymbols(@login) collate database_default + '" -P"' + fn_escapecmdshellsymbols(isnull(@password,'')) collate database_default + '" -S"' + fn_escapecmdshellsymbols(@@SERVERNAME) collate database_default + '" ' END select @osql_cmd = @osql_cmd + '-l60 -t60 ' -- We must use -b option to make osql return error code !! SELECT @osql_cmd = @osql_cmd + ' -d"' + fn_escapecmdshellsymbols(@database) collate database_default + '" -b ' + ' -i' + '"' + fn_escapecmdshellsymbolsremovequotes(@install_path) collate database_default + '\install\instdist.sql"' + ' -o' + '"' + fn_escapecmdshellsymbolsremovequotes(@install_path) collate database_default + '\install\instdist.out"' if (@osql_for_nt = 1) BEGIN SELECT @osql_cmd = @osql_cmd + ' "' END EXEC @retcode = master..xp_cmdshell @osql_cmd IF @retcode <> 0 OR @@ERROR <> 0 BEGIN RAISERROR (14113, 16, -1, @osql_cmd, 'instdist.out') GOTO UNDO END end if (@createmode <> @createmode_fordetach) begin /* Set db_existed bit in MSrepl_version */ IF @db_exists = 1 BEGIN SELECT @distproc = 'UPDATE ' + @database + '..MSrepl_version SET db_existed = 0x1' EXEC(@distproc) IF @@ERROR <> 0 BEGIN GOTO UNDO END END DELETE msdb.dbo.MSdistributiondbs WHERE name = @database collate database_default IF @@ERROR <> 0 BEGIN GOTO UNDO END INSERT INTO msdb.dbo.MSdistributiondbs VALUES ( @database, @min_distretention, @max_distretention, @history_retention ) IF @@ERROR <> 0 BEGIN GOTO UNDO END -- This login need db_owner priviledge to call sps in distribution db declare @distributor_login sysname select @distributor_login = 'distributor_admin' select @command = quotename(@database) + '.dbo.sp_MSrepl_dbrole' exec @retcode = @command 'db_owner', @distributor_login, 'add' IF @@error <> 0 OR @retcode <> 0 GOTO UNDO if @from_scripting = 0 begin /* ** Create the history cleanup agent. */ SELECT @agentname = formatmessage (20567, @database) SELECT @command = 'EXEC dbo.sp_MShistory_cleanup @history_retention = ' + CONVERT(nvarchar(12), @history_retention) IF EXISTS (SELECT * FROM msdb..sysjobs_view WHERE name = @agentname collate database_default and UPPER(originating_server) = UPPER(CONVERT(sysname, SERVERPROPERTY('ServerName')))) BEGIN EXEC @retcode = msdb.dbo.sp_delete_job @job_name = @agentname IF @@ERROR <> 0 or @retcode <> 0 BEGIN GOTO UNDO END END set @description = formatmessage(20535) -- Get History Cleanup category name (assumes category_id = 12) select @category_name = name FROM msdb.dbo.syscategories where category_id = 12 EXECUTE @retcode = dbo.sp_MSadd_repl_job @agentname, @subsystem = 'TSQL', @server = @@SERVERNAME, @databasename = @database, @description = @description, @freqtype = 4, @freqsubtype = 4, @freqsubinterval = 10, /* Number of minutes between runs */ @command = @command, @enabled = 1, @retryattempts = 0, @loghistcompletionlevel = 0, @category_name = @category_name IF @@ERROR <> 0 or @retcode <> 0 BEGIN GOTO UNDO END /* ** Create the distribution cleanup agent. */ SELECT @agentname = formatmessage (20568, @database) SELECT @command = 'EXEC dbo.sp_MSdistribution_cleanup @min_distretention = ' + CONVERT(nvarchar(12), @min_distretention) + ', @max_distretention = ' + CONVERT(nvarchar(12), @max_distretention) IF EXISTS (SELECT * FROM msdb..sysjobs_view WHERE name = @agentname collate database_default and UPPER(originating_server) = UPPER(CONVERT(sysname, SERVERPROPERTY('ServerName')))) BEGIN EXEC @retcode = msdb.dbo.sp_delete_job @job_name = @agentname IF @@ERROR <> 0 or @retcode <> 0 BEGIN GOTO UNDO END END set @description = formatmessage(20541) -- Get Distribution Cleanup category name (assumes category_id = 11) select @category_name = name FROM msdb.dbo.syscategories where category_id = 11 EXECUTE @retcode = msdb.dbo.sp_MSadd_repl_job @agentname, @subsystem = 'TSQL', @server = @@SERVERNAME, @databasename = @database, @description = @description, @freqtype = 4, @freqsubtype = 4, @freqsubinterval = 10, /* Number of minutes between runs */ @command = @command, @retryattempts = 0, @enabled = 0, @loghistcompletionlevel = 0, @category_name = @category_name, -- Start and end time is 5 min off from the history cleanup, which use the default. @activestarttimeofday = 000500, @activeendtimeofday = 000459 IF @@ERROR <> 0 or @retcode <> 0 BEGIN GOTO UNDO end end end else begin /*detach */ dbcc detachdb(@database) end RETURN(0) UNDO: IF @db_exists = 0 EXECUTE dbo.sp_dropdistributiondb @database /* Need to do it since sp_dropdistributiondb will fail in some cases */ UPDATE master..sysdatabases SET category = category & ~@distbit WHERE name = @database collate database_default DELETE msdb.dbo.MSdistributiondbs where name = @database collate database_default /* drop the database and ignore error */ IF @db_exists = 0 AND EXISTS (SELECT * from master..sysdatabases WHERE name = @database collate database_default) BEGIN SELECT @command = 'USE master DROP DATABASE ' + QUOTENAME(@database) EXEC (@command) END if (@createmode = @createmode_attach) begin if (@data_file_preexists = 0) begin select @command = 'del ' + @data_path_quoted_for_copy exec master..xp_cmdshell @command --ignore errors end if (@log_file_preexists = 0) begin select @command = 'del ' + @log_path_quoted_for_copy exec master..xp_cmdshell @command --ignore errors end end RETURN(1) GO if exists (select * from sysobjects where type = 'P' and name = 'sp_adddistpublisher') drop procedure sp_adddistpublisher raiserror('Creating procedure sp_adddistpublisher', 0,1) go CREATE PROCEDURE sp_adddistpublisher ( @publisher sysname, /* publisher server name */ @distribution_db sysname, @security_mode int = NULL, @login sysname = 'sa', @password sysname = NULL, @working_directory nvarchar(255), @trusted nvarchar(5) = NULL, @encrypted_password bit = 0, @thirdparty_flag bit = 0 ) AS SET NOCOUNT ON /* ** Declarations. */ DECLARE @retcode int DECLARE @reg_key nvarchar(255) DECLARE @distbit int DECLARE @active_value int DECLARE @server_added bit DECLARE @proc nvarchar(255) declare @fExists int declare @command nvarchar(255) declare @trusted_id bit declare @platform_nt binary declare @qv_replication varchar(10) declare @qv_replication_unlimited integer declare @qv_value_replication integer declare @enc_password nvarchar(524) select @platform_nt = 0x1 select @qv_replication = '2745196162', @qv_replication_unlimited = 0 SELECT @distbit = 16 SELECT @server_added = 0 /* ** Check if replication components are installed on this server */ exec @retcode = dbo.sp_MS_replication_installed if (@retcode <> 1) begin return (1) end IF @working_directory IS NULL or ltrim(rtrim(@working_directory)) = '' BEGIN RAISERROR (14043, 16, -1, '@working_directory') return (1) END /* ** Parameter Check: @publisher. ** Check to make sure that the publisher is not NULL and that it ** conforms to the rules for identifiers. */ IF @publisher IS NULL BEGIN RAISERROR (14043, 16, -1, '@publisher') return (1) END EXECUTE @retcode = dbo.sp_validname @publisher IF @@ERROR <> 0 OR @retcode <> 0 return (1) IF @password = N'' select @password = NULL /* On REPLICATION_LIMITED server, only local publisher is supported. * Note: The login and password registered for local publisher will be used for * local agents to login to distributor, thus local publisher has to be installed first. * We choose not to support remote dist publshers on REPLICATION_LIMITED server altogether. * On NT, local agents will always use integrated security to log into * distributor * Today, REPLICATION_LIMITED means desktop but we check specific sku entry just in case */ exec @qv_value_replication = master.dbo.sp_MSinstance_qv @qv_replication if ( @qv_value_replication != @qv_replication_unlimited ) and ( UPPER(@publisher) <> UPPER(@@servername) ) begin -- remote dist publisher is not supported on this server version raiserror(21041,16,-1) return (1) end -- Set default security IF @security_mode IS NULL BEGIN IF (UPPER(@publisher) = UPPER(@@SERVERNAME) and ( platform() & @platform_nt = @platform_nt ) ) SELECT @security_mode = 1 ELSE SELECT @security_mode = 0 END /* ** Check for invalid security mode */ IF @security_mode < 0 OR @security_mode > 1 BEGIN RAISERROR(14109, 16, -1) return (1) END IF (UPPER(@publisher) = UPPER(@@SERVERNAME) and ( @platform_nt != platform() & @platform_nt ) and @security_mode = 1) BEGIN RAISERROR(21038, 16, -1) RETURN (1) END -- Encrypt the password select @enc_password = @password IF @encrypted_password = 0 BEGIN EXEC @retcode = master.dbo.xp_repl_encrypt @enc_password OUTPUT IF @@error <> 0 OR @retcode <> 0 return 1 END -- Validate the working directory -- Remove heading and trailing spaces select @working_directory = RTRIM(LTRIM(@working_directory)) -- if the last char is '\', remove it. if substring(@working_directory, len(@working_directory),1) = '\' select @working_directory = substring(@working_directory, 1, len(@working_directory)-1) -- Don't do validation if it is a UNC path due to security problem. -- If the server is started as a service using local system account, we -- don't have access to the UNC path. if substring(@working_directory, 1,2) <> '\\' begin select @command = 'dir "' + fn_escapecmdshellsymbolsremovequotes(@working_directory) collate database_default + '"' exec @retcode = master..xp_cmdshell @command, 'no_output' if @@error <> 0 return (1) if @retcode <> 0 begin raiserror (21037, 16, -1, @working_directory) return (1) end end /* ** Parameter Check: @trusted */ if @trusted is null begin if UPPER(@publisher) = UPPER(@@servername) select @trusted = 'false' else select @trusted = 'true' end IF LOWER(@trusted collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true', 'false') BEGIN RAISERROR (14148, 16, -1, '@trusted') RETURN (1) END IF LOWER(@trusted collate SQL_Latin1_General_CP1_CS_AS) = 'true' SELECT @trusted_id = 1 ELSE SELECT @trusted_id = 0 /* ** Check to make sure this is a distributor */ IF NOT EXISTS (SELECT * FROM master..sysservers WHERE UPPER(datasource) = UPPER(@@SERVERNAME) collate database_default AND srvstatus & 8 <> 0) BEGIN RAISERROR (14114, 16, -1, @@SERVERNAME) return (1) END /* ** Check if database is configured as a distributor database */ IF NOT EXISTS (SELECT * FROM msdb..MSdistributiondbs WHERE name = @distribution_db collate database_default) BEGIN RAISERROR (14117, 16, -1, @distribution_db) return (1) END /* Check if publisher is already defined. */ IF EXISTS (SELECT * FROM msdb..MSdistpublishers WHERE UPPER(name) = UPPER(@publisher) collate database_default) BEGIN RAISERROR (14074, 16, -1, @publisher) RETURN (1) END IF NOT EXISTS (SELECT * FROM master..sysservers WHERE UPPER(srvname) = UPPER(@publisher) collate database_default) /* Add the server if it does not exist. */ BEGIN EXECUTE @retcode = dbo.sp_addserver @publisher IF @@error <> 0 OR @retcode <> 0 BEGIN RAISERROR (14075, 16, -1) GOTO UNDO END SELECT @server_added = 1 END ELSE BEGIN SELECT @publisher = fn_getpersistedservernamecasevariation(@publisher) collate database_default END /* ** Set the Active value. ** If the @publisher is local, set it to true. ** Otherwise, set it to false */ IF UPPER(@publisher) = UPPER(@@SERVERNAME) SELECT @active_value = 1 ELSE SELECT @active_value = 0 DELETE msdb.dbo.MSdistpublishers where UPPER(name) = UPPER(@publisher) collate database_default IF @@ERROR <> 0 BEGIN GOTO UNDO END INSERT INTO msdb.dbo.MSdistpublishers VALUES ( @publisher, @distribution_db, @working_directory, @security_mode, @login, @enc_password, @active_value, @trusted_id, @thirdparty_flag) IF @@ERROR <> 0 BEGIN GOTO UNDO END -- Add distributor_admin to distribution_admin non trusted mapping exec @fExists = dbo.sp_MSIfExistsRemoteLogin @publisher, null, 'distributor_admin' if( @fExists = 0 ) BEGIN EXECUTE @retcode = dbo.sp_addremotelogin @publisher, 'distributor_admin', 'distributor_admin' IF @@error <> 0 OR @retcode <> 0 BEGIN RAISERROR (14075, 16, -1) GOTO UNDO END END -- For 6x publisher, we still need the trusted sa to sa. -- For 6x publisher upgrading to 7.0, distributor_admin to distributor_admin need to be trusted. -- add remotelogin of SA if it doesn't already exist -- If there's a mapping for remote login sa already, we cannot map it to distributor_admin -- this is the case of server upgraded from 6.5. exec @fExists = dbo.sp_MSIfExistsRemoteLogin @publisher, null, 'sa' if( @fExists = 0 ) BEGIN EXECUTE @retcode = dbo.sp_addremotelogin @publisher, 'distributor_admin', 'sa' IF @@error <> 0 OR @retcode <> 0 BEGIN RAISERROR (14075, 16, -1) GOTO UNDO END END if @trusted_id = 1 begin exec @fExists = dbo.sp_MSIfExistsRemoteLogin @publisher, 'distributor_admin', 'sa' if( @fExists = 1 ) BEGIN EXECUTE @retcode = dbo.sp_remoteoption @publisher, 'distributor_admin', 'sa', trusted, true IF @@error <> 0 OR @retcode <> 0 BEGIN RAISERROR (14075, 16, -1) GOTO UNDO END END EXECUTE @retcode = dbo.sp_remoteoption @publisher, 'distributor_admin', 'distributor_admin', trusted, true IF @@error <> 0 OR @retcode <> 0 BEGIN RAISERROR (14075, 16, -1) GOTO UNDO END END /* Add remotelogin enabling the 'probe' of the publisher to ** RPC for distribution counter information. */ /* SECURITY ******************************** IF NOT EXISTS (SELECT * FROM master..sysremotelogins srl, master..sysservers ss WHERE UPPER(ss.srvname) = UPPER(@publisher) collate database_default AND srl.remoteserverid = ss.srvid AND srl.remoteusername = 'probe' AND srl.suid = 10) -- 'probe' exec @fExists = dbo.sp_MSIfExistsRemoteLogin @publisher, 'probe', 'probe' if (@fExists = 0) BEGIN EXECUTE @retcode = dbo.sp_addremotelogin @publisher, 'probe', 'probe' IF @@error <> 0 OR @retcode <> 0 BEGIN RAISERROR (14075, 16, -1) GOTO UNDO END END *********************************/ RETURN(0) UNDO: -- If the server is marked, drop it IF EXISTS (SELECT * FROM msdb..MSdistpublishers WHERE UPPER(name) = UPPER(@publisher) collate database_default) EXEC dbo.sp_dropdistpublisher @publisher IF @server_added = 1 EXEC dbo.sp_dropserver @publisher RETURN(1) GO if exists (select * from sysobjects where type = 'P' and name = 'sp_changedistpublisher') drop procedure sp_changedistpublisher raiserror('Creating procedure sp_changedistpublisher', 0,1) go CREATE PROCEDURE sp_changedistpublisher ( @publisher sysname, @property sysname = NULL, /* The property to change */ @value nvarchar(255) = NULL /* The new property value */ ) AS SET NOCOUNT ON /* ** Declarations. */ DECLARE @retcode int DECLARE @new_database sysname DECLARE @new_security_mode int DECLARE @new_login sysname DECLARE @new_password nvarchar(524) DECLARE @distbit int DECLARE @new_active int DECLARE @new_trusted bit DECLARE @command nvarchar(255) declare @distribdb sysname DECLARE @platform_nt binary SELECT @platform_nt = 0x1 SELECT @distbit = 16 /* ** Parameter Check: @property. ** If the @property parameter is NULL, print the options. */ IF @property IS NULL BEGIN CREATE TABLE #tab1 (properties sysname collate database_default not null) INSERT INTO #tab1 VALUES ('distribution_db') INSERT INTO #tab1 VALUES ('working_directory') INSERT INTO #tab1 VALUES ('security_mode') INSERT INTO #tab1 VALUES ('login') INSERT INTO #tab1 VALUES ('password') INSERT INTO #tab1 VALUES ('active') INSERT INTO #tab1 VALUES ('trusted') SELECT * FROM #tab1 RETURN (0) END /* ** Parameter Check: @property. ** Check to make sure that @property is a valid property in ** msdb.dbo.MSdistpublishers. */ IF @property IS NULL OR LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('distribution_db', 'working_directory', 'security_mode', 'login', 'password', 'active', 'trusted') BEGIN RAISERROR (14115, 16, -1, '''distribution_db'', ''working_directory'', ''security_mode'', ''login'', ''password'', ''active'', or ''trusted''') RETURN (1) END /* ** Check to make sure this is a distributor */ IF NOT EXISTS (SELECT * FROM master..sysservers WHERE UPPER(datasource) = UPPER(@@SERVERNAME) collate database_default AND srvstatus & 8 <> 0) BEGIN RAISERROR (14114, 16, -1, @@SERVERNAME) RETURN(1) END -- Get the distribution db name. select @distribdb = distribution_db from msdb..MSdistpublishers where UPPER(name) = UPPER(@publisher) collate database_default /* ** Change the property. */ IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'distribution_db' BEGIN IF @value IS NULL BEGIN RAISERROR (14043, 16, -1, '@value') RETURN (1) END IF @value <> @distribdb and EXISTS (SELECT * FROM msdb.dbo.MSdistpublishers WHERE UPPER(name) = UPPER(@publisher) collate database_default and active = 1) BEGIN RAISERROR (21046, 16, -1) RETURN (1) END /* ** Check if database is configured as a distributor database */ IF NOT EXISTS (SELECT * FROM master..sysdatabases WHERE name = @value collate database_default AND category & @distbit <> 0) BEGIN RAISERROR (14117, 16, -1, @new_database) RETURN(1) END UPDATE msdb..MSdistpublishers SET distribution_db = @value WHERE UPPER(name) = UPPER(@publisher) collate database_default IF @@error <> 0 BEGIN RETURN (1) END END IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'working_directory' BEGIN IF @value IS NULL BEGIN RAISERROR (14043, 16, -1, '@value') RETURN (1) END -- Validate the working directory -- Remove heading and trailing spaces select @value = RTRIM(LTRIM(@value)) -- if the last char is '\', remove it. if substring(@value, len(@value),1) = '\' select @value = substring(@value, 1, len(@value)-1) -- Don't do validation if it is a UNC path due to security problem. -- If the server is started as a service using local system account, we -- don't have access to the UNC path. if substring(@value, 1,2) <> '\\' begin select @command = 'dir "' + fn_escapecmdshellsymbolsremovequotes(@value) collate database_default + N'"' exec @retcode = master..xp_cmdshell @command, 'no_output' if @@error <> 0 return 1 if @retcode <> 0 begin raiserror (21037, 16, -1, @value) return 1 end end UPDATE msdb..MSdistpublishers SET working_directory = @value WHERE UPPER(name) = UPPER(@publisher) collate database_default IF @@error <> 0 BEGIN RETURN (1) END END IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'security_mode' BEGIN IF @value IS NULL BEGIN RAISERROR (14043, 16, -1, '@value') RETURN (1) END /* ** Set the SecurityMode registry key value */ SELECT @new_security_mode = CONVERT(int, @value) /* ** Check for invalid values */ IF @new_security_mode < 0 OR @new_security_mode > 1 BEGIN RAISERROR(14109, 16, -1) RETURN (1) END IF (UPPER(@publisher) = UPPER(@@SERVERNAME) and ( @platform_nt != platform() & @platform_nt ) and @new_security_mode = 1) BEGIN RAISERROR(21038, 16, -1) RETURN (1) END UPDATE msdb..MSdistpublishers SET security_mode = @new_security_mode WHERE UPPER(name) = UPPER(@publisher) collate database_default IF @@error <> 0 BEGIN RETURN (1) END END IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'login' BEGIN IF @value IS NULL BEGIN RAISERROR (14043, 16, -1, '@value') RETURN (1) END /* ** Set the Login registry key value */ SELECT @new_login = CONVERT(sysname, @value) UPDATE msdb..MSdistpublishers SET login = @new_login WHERE UPPER(name) = UPPER(@publisher) collate database_default IF @@error <> 0 BEGIN RETURN (1) END END IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'password' BEGIN /* ** Set the Password registry key value */ SELECT @new_password = CONVERT(nvarchar(524), @value) -- Encrypt the password EXEC @retcode = master.dbo.xp_repl_encrypt @new_password OUTPUT IF @@error <> 0 OR @retcode <> 0 RETURN (1) UPDATE msdb..MSdistpublishers SET password = @new_password WHERE UPPER(name) = UPPER(@publisher) collate database_default IF @@error <> 0 BEGIN RETURN (1) END END IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'active' BEGIN /* ** Check for a valid value. */ IF LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true', 'false') BEGIN RAISERROR (14137, 16, -1) RETURN (1) END IF LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'true' begin -- Clean up the database in case of the remote publisher is reinstalling publishing. SELECT @command = @distribdb + '.dbo.sp_MSdistpublisher_cleanup' exec @retcode = @command @publisher if @retcode <> 0 or @@error <> 0 return 1 SELECT @new_active = 1 end ELSE BEGIN SELECT @new_active = 0 END /* ** Set the Active registry key value */ UPDATE msdb..MSdistpublishers SET active = @new_active WHERE UPPER(name) = UPPER(@publisher) collate database_default IF @@error <> 0 BEGIN RETURN (1) END END IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'trusted' BEGIN /* ** Check for a valid value. */ IF LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true', 'false') BEGIN RAISERROR (14137, 16, -1) RETURN (1) END declare @fExists int IF LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'true' begin SELECT @new_trusted = 1 exec @fExists = dbo.sp_MSIfExistsRemoteLogin @publisher, 'distributor_admin', 'sa' if( @fExists = 1 ) BEGIN EXECUTE @retcode = dbo.sp_remoteoption @publisher, 'distributor_admin', 'sa', trusted, true IF @@error <> 0 OR @retcode <> 0 BEGIN RAISERROR (14075, 16, -1) RETURN (1) END END EXECUTE @retcode = dbo.sp_remoteoption @publisher, 'distributor_admin', 'distributor_admin', trusted, true IF @@error <> 0 OR @retcode <> 0 BEGIN RAISERROR (14075, 16, -1) RETURN (1) END end ELSE BEGIN SELECT @new_trusted = 0 exec @fExists = dbo.sp_MSIfExistsRemoteLogin @publisher, 'distributor_admin', 'sa' if( @fExists = 1 ) BEGIN EXECUTE @retcode = dbo.sp_remoteoption @publisher, 'distributor_admin', 'sa', trusted, false IF @@error <> 0 OR @retcode <> 0 BEGIN RAISERROR (14075, 16, -1) RETURN (1) END END EXECUTE @retcode = dbo.sp_remoteoption @publisher, 'distributor_admin', 'distributor_admin', trusted, 'false' IF @@error <> 0 OR @retcode <> 0 BEGIN RAISERROR (14075, 16, -1) RETURN (1) END END /* ** Set the trusted property */ UPDATE msdb..MSdistpublishers SET trusted = @new_trusted WHERE UPPER(name) = UPPER(@publisher) collate database_default IF @@error <> 0 BEGIN RETURN (1) END END /* ** Return succeed. */ RAISERROR (21035, 10, -1, @property) DONE: RETURN (0) go if exists (select * from sysobjects where type = 'P' and name = 'sp_MScopyscriptfile') drop procedure sp_MScopyscriptfile go create proc sp_MScopyscriptfile ( @scriptfile nvarchar(4000), @cmd nvarchar(4000) OUTPUT ) as declare @directory nvarchar(4000) declare @filename nvarchar(1024) declare @subdirectory nvarchar(1024) declare @retcode int IF @scriptfile IS NULL BEGIN RAISERROR (14043, 16, -1, '@scriptfile') RETURN (1) END -- Create the directory on distributor to store script. exec sp_helpdistributor @directory=@directory OUTPUT select @subdirectory = convert(nvarchar(64), GetDate(), 121) select @subdirectory = replace(@subdirectory, N'-', N'') select @subdirectory = replace(@subdirectory, N' ', N'') select @subdirectory = replace(@subdirectory, N':', N'') select @subdirectory = replace(@subdirectory, N'.', N'') if(right(@directory, 1) = N'\') select @directory = @directory + @subdirectory else select @directory = @directory + N'\' + @subdirectory select @cmd = N'md "' + fn_escapecmdshellsymbolsremovequotes(@directory) collate database_default + '"' exec @retcode = master..xp_cmdshell @cmd, NO_OUTPUT if(@retcode <> 0) begin raiserror(21330, 16, -1, @cmd) return (1) end -- Copy script to distributor select @cmd = N'copy "' + fn_escapecmdshellsymbolsremovequotes(@scriptfile) collate database_default + N'" "' + fn_escapecmdshellsymbolsremovequotes(@directory) collate database_default + N'"' exec @retcode = master..xp_cmdshell @cmd, NO_OUTPUT if(@retcode <> 0) begin raiserror(21331, 16, -1, @cmd) return (1) end -- Prepare command, to be posted to log select @filename = right(@scriptfile, charindex(N'\', reverse(@scriptfile))) if(left(@filename, 1) = N'\') select @cmd = @directory + @filename else select @cmd = @directory + N'\' + @filename go exec sp_MS_marksystemobject sp_MScopyscriptfile go -------------------------------------------------------------------------------- --. System objects (rladmin.sql) -------------------------------------------------------------------------------- if exists (select * from sysobjects where type in ('P ') and name = 'sp_MSremove_userscript') drop procedure sp_MSremove_userscript go create procedure sp_MSremove_userscript( @pubid uniqueidentifier, @drop_publication bit = 0 )as declare @retention int declare @last_snapshot datetime declare @post_snapshot_ver int declare @post_snapshot_type int declare @user_script_type int declare @retcode int declare @len int declare @file_path nvarchar(4000) declare @delfile_cmd nvarchar(4000) declare @rmdir_cmd nvarchar(4000) select @post_snapshot_type=52 select @user_script_type=46 if not exists (select * from sysmergeschemachange where pubid=@pubid and schematype=@user_script_type) return (0) select @retention=retention from sysmergepublications where pubid=@pubid select @last_snapshot=last_validated from sysmergesubscriptions where pubid=@pubid and subid=@pubid --I do not want to remove script files by setting retention to 0 if (@retention=0 or dateadd(day, -@retention, getdate()) < @last_snapshot) and @drop_publication = 0 return (0) select @post_snapshot_ver=schemaversion from sysmergeschemachange where schematype=@post_snapshot_type and pubid=@pubid --only get those script that can be safely removed declare #per_script cursor local fast_forward for select schematext from sysmergeschemachange where pubid=@pubid and schematype=@user_script_type and (schemaversion<@post_snapshot_ver or @drop_publication = 1) open #per_script fetch #per_script into @file_path while (@@fetch_status<>-1) begin if(left(@file_path, 1) = N'0' or left(@file_path, 1) = N'1') select @file_path = right(@file_path, len(@file_path) - 1) select @delfile_cmd = N'del "' + fn_escapecmdshellsymbolsremovequotes(@file_path) collate database_default + N'"' EXEC @retcode = master..xp_cmdshell @delfile_cmd, NO_OUTPUT if @@ERROR<>0 goto FAILURE select @len=CHARINDEX ( '\' , reverse(@file_path) ) select @file_path=SUBSTRING(@file_path , 1 , len(@file_path)-@len + 1) select @delfile_cmd = N'rmdir "' + fn_escapecmdshellsymbolsremovequotes(@file_path) collate database_default + N'"' EXEC @retcode = master..xp_cmdshell @delfile_cmd, NO_OUTPUT if @@ERROR<>0 goto FAILURE fetch next from #per_script into @file_path end close #per_script deallocate #per_script return (0) FAILURE: close #per_script deallocate #per_script return (1) go EXEC dbo.sp_MS_marksystemobject 'sp_MSremove_userscript' grant execute on sp_MSremove_userscript to public go exec sp_MS_upd_sysobj_category 2 go exec sp_configure 'allow updates',0 go reconfigure with override go -------------------------------------------------------------------------------- -- qfe361327 -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- -- VERIFY Server is started in single-user-mode (catalog-updates enabled), and -- start marking of system-objects. -------------------------------------------------------------------------------- use master go dump tran master with no_log go exec dbo.sp_configure 'allow updates',1 go reconfigure with override go set ANSI_NULLS off exec sp_MS_upd_sysobj_category 1 go if exists (select * from sysobjects where name = 'xp_execresultset') revoke execute on dbo.xp_execresultset to public if exists (select * from sysobjects where name = 'xp_displayparamstmt') revoke execute on dbo.xp_displayparamstmt to public if exists (select * from sysobjects where name = 'xp_printstatements') revoke execute on dbo.xp_printstatements to public exec sp_MS_upd_sysobj_category 2 go exec sp_configure 'allow updates',0 go reconfigure with override go -------------------------------------------------------------------------------- -- qfe361713 -------------------------------------------------------------------------------- -------------------------------------------------------------- -- BEGIN CHANGES TO REPLCOM.SQL -------------------------------------------------------------- USE master go if object_id('sp_replproberemoteserver', 'P') is not null begin drop procedure sp_replproberemoteserver end go raiserror('Creating procedure sp_replproberemoteserver', 0,1) go -- -- Name: sp_replproberemoteserver -- -- Description: This is a lightweight wrapper for calling xp_replproberremsrv -- with the added nicety of looking up the agent command line -- using the given jobid. This procedure supports two different -- modes of operation based on the @no_rpc parameter. If the -- @no_rpc parameter is 1, this procedure will not attempt to make -- rpc call to the Distributor. The local mode is mainly useful -- for verifying remote pull subscription agent. -- -- Parameters: @remoteservername sysname (mandatory) -- @agent_type nvarchar(16) (optional, default = null) -- @agent_jobid uniqueidentifier (optional default = null) -- @no_rpc bit (optional default = 0) -- -- Notes: If @job_id is null, only activation verification will be carried -- out. -- -- Security: Execute permission of this procedure is granted to public -- -- Result: 'probe_succeeded' 0 or 1 -- -- Returns: 0 - succeeded -- <> 0 - failed -- create procedure dbo.sp_replproberemoteserver ( @remoteservername sysname, @agent_type nvarchar(16) = null, @agent_jobid uniqueidentifier = null, @no_rpc bit = 0 ) as begin set nocount on declare @retcode int declare @commandline nvarchar(3200) declare @distributor sysname declare @distributiondb sysname declare @distproc nvarchar(255) declare @succeeded nvarchar(10) select @retcode = 0 select @commandline = null select @succeeded = null -- security check, db_owner -- Note that this proc can be called from either a publisher database or -- a subscriber database exec @retcode = dbo.sp_MSreplcheck_publish if @@error <> 0 or @retcode <> 0 return (1) if @agent_type is null begin select @agent_type = 'distribution' end -- @remoteservername cannot be null or empty select @remoteservername = rtrim(ltrim(@remoteservername)) if @remoteservername is null or @remoteservername = N'' begin raiserror(21263,16,-1,'@remoteservername') return 1 end -- @agent_type must be 'distribution' or 'merge' if @agent_type not in (N'distribution', N'merge') begin raiserror(21182,16,-1) return 1 end -- Get Distributor information if @no_rpc = 0 begin exec @retcode = dbo.sp_helpdistributor @rpcsrvname = @distributor output, @distribdb = @distributiondb output if @@error <> 0 or @retcode <> 0 return 1 end else begin select @distributor = @@servername end if upper(@@servername) <> upper(@distributor) begin select @distproc = ltrim(rtrim(@distributor)) + '.' + 'master.dbo.sp_replproberemoteserver' exec @retcode = @distproc @remoteservername, @agent_type, @agent_jobid, 1 return @retcode end else begin -- If the given @job_id is not null, try to get -- the agent command line if @agent_jobid is not null begin select @commandline = fn_replgetagentcommandlinefromjobid( @agent_type, @agent_jobid) collate database_default if @commandline is null begin raiserror(21361,6,-1, @agent_type) select 'probe_succeeded' = 0 return 1 end end exec @retcode = master.dbo.xp_replproberemsrv @remoteservername, @agent_type, @succeeded output, @commandline if lower(@succeeded collate SQL_Latin1_General_CP1_CS_AS) = N'true' select 'probe_succeeded' = 1 else select 'probe_succeeded' = 0 return @retcode end select 'probe_succeeded' = 0 return 1 end go exec sp_MS_marksystemobject sp_replproberemoteserver go grant execute on dbo.sp_replproberemoteserver to public go revoke execute on dbo.xp_replproberemsrv to public go -------------------------------------------------------------------------------- -- QFE361403 changes - repltran.sql and replsys.sql -------------------------------------------------------------------------------- set ANSI_NULLS off go exec dbo.sp_configure 'update',1 go reconfigure with override go -------------------------------------------------------------------------------- --. sp_MSisnonpkukupdateinconflict -------------------------------------------------------------------------------- if exists (select * from sysobjects where type = 'P' and name = 'sp_MSisnonpkukupdateinconflict') drop procedure sp_MSisnonpkukupdateinconflict go raiserror('Creating procedure sp_MSisnonpkukupdateinconflict', 0,1) go create proc sp_MSisnonpkukupdateinconflict ( @pubid int ,@artid int ,@bitmap varbinary(4000) ) as begin declare @retcode int ,@columns binary(32) ,@tabname sysname ,@tabid int ,@indid int ,@indkey int ,@key sysname ,@colid int ,@isset int ,@artcol int ,@bytepos int ,@bitpos int declare @ukcoltab table(ukindex int identity, keyname sysname collate database_default not null) -- -- security check -- exec @retcode = dbo.sp_MSreplcheck_publish if @@ERROR != 0 or @retcode != 0 return (1) -- -- initalize and validate -- select @columns = [columns] ,@tabid = objid ,@artcol = 0 from dbo.sysarticles where (artid = @artid) and (pubid = @pubid) -- -- validate article -- if (@tabid is null) begin raiserror(21344, 16, -1, '@pubid, @artid') return -1 end -- -- the table should have non PK unique keys -- exec @retcode = dbo.sp_repltablehasnonpkuniquekey @tabid if (@retcode != 1) begin return 0 end -- -- get fully qualified table -- select @tabname = QUOTENAME(user_name(OBJECTPROPERTY(@tabid, 'OwnerId'))) collate database_default + N'.' + QUOTENAME(object_name( @tabid )) collate database_default -- -- get the non PK unique indices -- declare #hcindid cursor local fast_forward for select indid from sysindexes where id = @tabid and (status & 2) != 0 and (status & 2048) = 0 and indid > 0 and indid < 255 order by indid asc open #hcindid fetch #hcindid into @indid while (@@fetch_status != -1) begin -- -- create an enumeration of all the columns -- that are part of selected unique index -- select @indkey = 1 while (@indkey <= 16) begin select @key = index_col( @tabname, @indid, @indkey ) if (@key is null) break else begin if not exists (select * from @ukcoltab where keyname = @key) insert into @ukcoltab(keyname) values(@key) end select @indkey = @indkey + 1 end -- -- fetch next index -- fetch #hcindid into @indid end close #hcindid deallocate #hcindid -- -- now walk through each article col and if it is -- a part of any of the unique keys, then check if the update bitmap bit -- corresponding to any article column is set -- declare #hccolid cursor local fast_forward for select colid, [name] from dbo.syscolumns where id = @tabid order by colid asc open #hccolid fetch #hccolid INTO @colid, @key while (@@fetch_status != -1) begin exec @isset = dbo.sp_isarticlecolbitset @colid, @columns if (@isset != 0) begin -- -- this column is part of the article -- select @artcol = @artcol + 1 if exists (select * from @ukcoltab where keyname = @key) begin -- -- this column is part of an unique key -- select @bytepos = 1 + (@artcol-1) / 8 ,@bitpos = power(2, (@artcol-1) % 8 ) -- -- if the update bitmap has bit set then -- then it is a nonPK key update -- if ((substring(@bitmap, @bytepos, 1) & @bitpos) = @bitpos) return 1 end end -- -- get the next column -- fetch #hccolid INTO @colid, @key end close #hccolid deallocate #hccolid -- -- if we have reached here then it mean the update does not -- affect PK columns, cleanup and return -- return 0 end go exec dbo.sp_MS_marksystemobject sp_MSisnonpkukupdateinconflict go grant execute on dbo.sp_MSisnonpkukupdateinconflict to public go -------------------------------------------------------------------------------- --. sp_scriptpubwinsrefreshcursorvars -------------------------------------------------------------------------------- if exists (select * from sysobjects where type = 'P' and name = 'sp_scriptpubwinsrefreshcursorvars') drop procedure sp_scriptpubwinsrefreshcursorvars go raiserror('Creating procedure sp_scriptpubwinsrefreshcursorvars', 0,1) go create procedure sp_scriptpubwinsrefreshcursorvars ( @objid int -- object id ) as begin declare @rc int ,@cmd nvarchar(4000) ,@colname sysname ,@ccoltype sysname ,@this_col int ,@typestring nvarchar(100) ,@spacer nvarchar(5) ,@isset int ,@pkcolumns varbinary(32) select @cmd = N' declare ' ,@spacer = N'' exec dbo.sp_getarticlepkcolbitmap @objid, @pkcolumns output declare #hccolid cursor local fast_forward for select colid from syscolumns where id = @objid order by colid asc open #hccolid fetch #hccolid into @this_col while (@@fetch_status != -1) begin exec @isset = dbo.sp_isarticlecolbitset @this_col, @pkcolumns if @isset != 0 and exists ( select name from syscolumns where id=@objid and colid=@this_col and iscomputed !=1 ) begin exec dbo.sp_gettypestring @objid, @this_col, @typestring output select @cmd = @cmd + @spacer + N'@pkc' + convert( nvarchar, @this_col ) + N' ' + @typestring ,@spacer = N',' if len( @cmd ) > 3000 begin insert into #proctext(procedure_text) values( @cmd ) select @cmd = N'' end end fetch #hccolid into @this_col end close #hccolid deallocate #hccolid if len(@cmd) > 0 insert into #proctext(procedure_text) values( @cmd ) -- -- all done -- return 0 end go exec dbo.sp_MS_marksystemobject sp_scriptpubwinsrefreshcursorvars go -------------------------------------------------------------------------------- --. sp_MSscriptdelconflictfinder -------------------------------------------------------------------------------- if exists (select * from sysobjects where type = 'P' and name = 'sp_MSscriptdelconflictfinder') drop procedure sp_MSscriptdelconflictfinder go raiserror('Creating procedure sp_MSscriptdelconflictfinder', 0,1) go create procedure sp_MSscriptdelconflictfinder ( @publication sysname -- publication name ,@article sysname -- article name ,@objid int -- object id ,@columns binary(32) -- columns replicated ) as begin declare @rc int ,@cmd nvarchar(4000) ,@artid int ,@pubid int ,@qualname nvarchar(512) ,@fhasnonpkuniquekeys int -- -- initialize the vars we will use -- select @pubid = pubid from syspublications where name = @publication select @artid = artid from sysarticles where name = @article and pubid = @pubid exec sp_MSget_qualified_name @objid, @qualname OUTPUT -- -- check if this article has non PK unique keys -- exec @fhasnonpkuniquekeys = dbo.sp_repltablehasnonpkuniquekey @tabid = @objid -- -- start scripting -- select @cmd = N' '+N'-- '+N'-- -------------------------------------------------------------------- '+N'-- This is the crux of the proc for conflict resolution '+N'-- This code block is essentially a state machine '+N'-- where we ascertain the state of resolution '+N'-- The actions of this resolution varies for the policy '+N'-- The comments for each state outline the policy '+N'-- specific actions '+N'-- -------------------------------------------------------------------- '+N'-- ' insert into #proctext(procedure_text) values( @cmd ) -- -- continue scripting -- select @cmd = N' if (@execution_mode in (@QPubWins, @QSubWins)) begin '+N'-- '+N'-- initialize the conflict case '+N'-- select @cftcase = 0 if (@rowcount = 0) begin '+N'-- '+N'-- row was deleted or updated '+N'--' insert into #proctext(procedure_text) values( @cmd ) -- -- script row exists with OLD_PK -- select @cmd = N' if exists (select * from ' + @qualname insert into #proctext(procedure_text) values( @cmd ) exec dbo.sp_MSscript_where_clause @objid, @columns, 'upd version', NULL, 0 insert into #proctext(procedure_text) values( N' )') -- -- continue scripting -- select @cmd = N' begin '+N'-- '+N'-- Case 31: Conflict as row was updated '+N'-- PubWins ----------------------------------------------------------- '+N'-- generate delete + insert compensating action with values for all unique keys '+N'-- SubWins ----------------------------------------------------------- '+N'-- delete row with PK '+N'-- select @cftcase = 31 end' insert into #proctext(procedure_text) values( @cmd ) -- -- continue scripting -- select @cmd = N' else begin '+N'-- '+N'-- Case 33: Conflict as row does not exist '+N'-- PubWins ----------------------------------------------------------- '+N'-- do nothing '+N'-- SubWins ----------------------------------------------------------- '+N'-- do nothing '+N'-- select @cftcase = 33 end end' insert into #proctext(procedure_text) values( @cmd ) -- -- continue scripting -- select @cmd = N' else if (@execution_mode = @QPubWins) begin '+N'-- '+N'-- we had no conflict for this command '+N'-- We need to process this block only in the Publisher Wins cases '+N'-- '+N'-- '+N'-- case 30: No conflict - we have to undo the delete '+N'-- PubWins ----------------------------------------------------------- '+N'-- generate delete + insert compensating action with values for all unique keys '+N'-- select @cftcase = 30 end end' insert into #proctext(procedure_text) values( @cmd ) -- -- continue scripting -- select @cmd = N' '+N'-- '+N'-- -------------------------------------------------------------------- '+N'-- Now the generation phase '+N'-- Use the conflict case value to decide what to do '+N'-- -------------------------------------------------------------------- '+N'--' insert into #proctext(procedure_text) values( @cmd ) -- -- all done -- return 0 end go exec dbo.sp_MS_marksystemobject sp_MSscriptdelconflictfinder go -------------------------------------------------------------------------------- --. sp_script_insertforcftresolution -------------------------------------------------------------------------------- if exists (select * from sysobjects where type = 'P' and name = 'sp_script_insertforcftresolution') drop procedure sp_script_insertforcftresolution go raiserror('Creating procedure sp_script_insertforcftresolution', 0,1) go create procedure sp_script_insertforcftresolution ( @objid int -- object id ,@columns binary(32) -- columns replicated ,@identity_insert bit -- enable identity insert ,@prefix nvarchar(10)=N'@c' -- prefix ,@suffix nvarchar(10)=NULL -- suffix ) as begin declare @cmd nvarchar(4000) ,@qualname nvarchar(512) ,@column_string nvarchar(4000) ,@var_string nvarchar(4000) ,@colname sysname ,@ccoltype sysname ,@this_col int ,@rc int ,@num_col int declare @worktab table( c1 int identity NOT NULL, procedure_text nvarchar(4000) collate database_default null) declare @worktab2 table( c1 int identity NOT NULL, procedure_text nvarchar(4000) collate database_default null) -- -- initialize -- exec sp_MSget_qualified_name @objid, @qualname OUTPUT -- -- prepare the assignments and column list for -- insert statement -- select @num_col = 0 DECLARE hCColid CURSOR LOCAL FAST_FORWARD FOR select colid from syscolumns where id = @objid order by colid asc OPEN hCColid FETCH hCColid INTO @this_col WHILE (@@fetch_status != -1) begin exec @rc = dbo.sp_MSget_colinfo @objid, @this_col, @columns, 0, @colname output, @ccoltype output if @rc = 0 and EXISTS (select name from syscolumns where id=@objid and colid=@this_col and iscomputed<>1) begin if rtrim(@ccoltype) not like N'timestamp' begin select @num_col = @num_col + 1 ,@column_string = quotename(@colname) ,@var_string = @prefix + cast(@this_col as nvarchar(4)) if (@suffix is not null) select @var_string = @var_string + @suffix if (@num_col > 1) begin select @column_string = N', ' + @column_string ,@var_string = N', ' +@var_string end insert into @worktab(procedure_text) values( @column_string ) insert into @worktab2(procedure_text) values( @var_string ) end end FETCH hCColid INTO @this_col end CLOSE hCColid DEALLOCATE hCColid if (@num_col > 0) begin if (@identity_insert = 1) begin -- Only to call set if identity is not marked for 'not for repl' -- This is to avoid security failure of 'SET' for PAL users if not exists (select * from syscolumns where id = @objid and ColumnProperty(id, name, 'IsIdNotForRepl') = 1) begin select @cmd = N' set identity_insert ' + @qualname + N' on ' insert into #proctext(procedure_text) values( @cmd ) end end select @cmd = N' insert into ' + @qualname + N'( ' insert into #proctext(procedure_text) values( @cmd ) insert into #proctext(procedure_text) select procedure_text from @worktab order by c1 asc select @cmd = N' ) values ( ' insert into #proctext(procedure_text) values( @cmd ) insert into #proctext(procedure_text) select procedure_text from @worktab2 order by c1 asc select @cmd = N' )' insert into #proctext(procedure_text) values( @cmd ) if (@identity_insert = 1) begin -- Only to call set if identity is not marked for 'not for repl' -- This is to avoid security failure of 'SET' for PAL users if not exists (select * from syscolumns where id = @objid and ColumnProperty(id, name, 'IsIdNotForRepl') = 1) begin select @cmd = N' set identity_insert ' + @qualname + N' off ' insert into #proctext(procedure_text) values( @cmd ) end end end -- -- all done -- return 0 end go exec dbo.sp_MS_marksystemobject sp_script_insertforcftresolution go -------------------------------------------------------------------------------- --. sp_MSscriptinsertconflictfinder -------------------------------------------------------------------------------- if exists (select * from sysobjects where type = 'P' and name = 'sp_MSscriptinsertconflictfinder') drop procedure sp_MSscriptinsertconflictfinder go raiserror('Creating procedure sp_MSscriptinsertconflictfinder', 0,1) go create procedure sp_MSscriptinsertconflictfinder ( @publication sysname -- publication name ,@article sysname -- article name ,@objid int -- object id ,@columns binary(32) -- columns replicated ) as begin declare @rc int ,@cmd nvarchar(4000) ,@artid int ,@pubid int ,@qualname nvarchar(512) ,@fhasnonpkuniquekeys int -- -- initialize the vars we will use -- select @pubid = pubid from syspublications where name = @publication select @artid = artid from sysarticles where name = @article and pubid = @pubid exec sp_MSget_qualified_name @objid, @qualname OUTPUT -- -- check if this article has non PK unique keys -- exec @fhasnonpkuniquekeys = dbo.sp_repltablehasnonpkuniquekey @tabid = @objid -- -- start scripting -- select @cmd = N' '+N'-- '+N'-- -------------------------------------------------------------------- '+N'-- This is the crux of the proc for conflict resolution '+N'-- This code block is essentially a state machine '+N'-- where we ascertain the state of resolution '+N'-- The actions of this resolution varies for the policy '+N'-- The comments for each state outline the policy '+N'-- specific actions '+N'-- -------------------------------------------------------------------- '+N'--' insert into #proctext(procedure_text) values( @cmd ) -- -- continue scripting -- select @cmd = N' if (@execution_mode in (@QPubWins, @QSubWins)) begin '+N'-- '+N'-- initialize the conflict case '+N'-- select @cftcase = 0 if (@rowcount = 0) begin '+N'-- '+N'-- we had conflict for this command '+N'-- if (@error in (547, 2601, 2627)) begin' insert into #proctext(procedure_text) values( @cmd ) select @cmd = N' '+N'-- '+N'-- Conflict due to unique key/constraint '+N'--' insert into #proctext(procedure_text) values( @cmd ) -- -- script row check with PK -- select @cmd = N' if exists (select * from ' + @qualname insert into #proctext(procedure_text) values( @cmd ) exec dbo.sp_MSscript_where_clause @objid, @columns, 'new_pk_q', NULL, 0 insert into #proctext(procedure_text) values( N' )') -- -- continue scripting -- select @cmd = N' begin '+N'-- '+N'-- case 21: A row with same PK already exists '+N'-- PubWins ----------------------------------------------------------- '+N'-- generate delete compensating action for row with PK '+N'-- generate delete + insert compensating action with values for all unique keys '+N'-- SubWins ----------------------------------------------------------- '+N'-- delete rows with values of all keys '+N'-- insert row with values '+N'-- select @cftcase = 21 end' insert into #proctext(procedure_text) values( @cmd ) -- -- script this block if the article has non PK unique keys -- if (@fhasnonpkuniquekeys = 1) begin select @cmd = N' else begin '+N'-- '+N'-- case 22: A row with same nonPK key(s) already exists '+N'-- PubWins ----------------------------------------------------------- '+N'-- generate delete compensating action for row with PK '+N'-- generate delete + insert compensating action with values for non PK keys '+N'-- SubWins ----------------------------------------------------------- '+N'-- delete rows with values of non PK keys '+N'-- insert row with values '+N'-- select @cftcase = 22 end' insert into #proctext(procedure_text) values( @cmd ) end -- -- continue scripting -- select @cmd = N' end end else if (@execution_mode = @QPubWins) begin '+N'-- '+N'-- we had no conflict for this command '+N'-- We need to process this block only in the Publisher Wins cases '+N'-- '+N'-- '+N'-- case 23: No conflict '+N'-- PubWins ----------------------------------------------------------- '+N'-- generate delete compensating action with PK '+N'-- select @cftcase = 23 end end' insert into #proctext(procedure_text) values( @cmd ) -- -- continue scripting -- select @cmd = N' '+N'-- '+N'-- -------------------------------------------------------------------- '+N'-- Now the generation phase '+N'-- Use the conflict case value to decide what to do '+N'-- -------------------------------------------------------------------- '+N'--' insert into #proctext(procedure_text) values( @cmd ) -- -- all done -- return 0 end go exec dbo.sp_MS_marksystemobject sp_MSscriptinsertconflictfinder go -------------------------------------------------------------------------------- --. sp_MSscriptupdateconflictfinder -------------------------------------------------------------------------------- if exists (select * from sysobjects where type = 'P' and name = 'sp_MSscriptupdateconflictfinder') drop procedure sp_MSscriptupdateconflictfinder go raiserror('Creating procedure sp_MSscriptupdateconflictfinder', 0,1) go create procedure sp_MSscriptupdateconflictfinder ( @publication sysname -- publication name ,@article sysname -- article name ,@objid int -- object id ,@columns binary(32) -- columns replicated ) as begin declare @rc int ,@cmd nvarchar(4000) ,@artid int ,@pubid int ,@qualname nvarchar(512) ,@fhasnonpkuniquekeys int -- -- initialize the vars we will use -- select @pubid = pubid from syspublications where name = @publication select @artid = artid from sysarticles where name = @article and pubid = @pubid exec sp_MSget_qualified_name @objid, @qualname OUTPUT -- -- check if this article has non PK unique keys -- exec @fhasnonpkuniquekeys = dbo.sp_repltablehasnonpkuniquekey @tabid = @objid -- -- start scripting -- select @cmd = N' ' + N'-- ' + N'-- -------------------------------------------------------------------- ' + N'-- This is the crux of the proc for conflict resolution ' + N'-- This code block is essentially a state machine ' + N'-- where we ascertain the state of resolution ' + N'-- The actions of this resolution varies for the policy ' + N'-- The comments for each state outline the policy ' + N'-- specific actions (I have only outlined Pub Wins for ' + N'-- now) ' + N'-- -------------------------------------------------------------------- ' + N'--' insert into #proctext(procedure_text) values( @cmd ) select @cmd = N' if (@execution_mode in (@QPubWins, @QSubWins)) begin declare @fpkeyupdated int' insert into #proctext(procedure_text) values( @cmd ) -- -- non PK unique keys specific scripting -- if (@fhasnonpkuniquekeys = 1) begin select @cmd = N' declare @fnpukeyupdated int' insert into #proctext(procedure_text) values( @cmd ) end -- -- continue scripting -- select @cmd = N' ' + N'-- ' + N'-- initialize the conflict case ' + N'-- select @cftcase = 0 ' insert into #proctext(procedure_text) values( @cmd ) -- -- script the PK update check -- select @cmd = N' exec @fpkeyupdated = dbo.sp_MSispkupdateinconflict ' + cast(@pubid as nvarchar(10)) + N', ' + cast(@artid as nvarchar(10)) + N', @bitmap' insert into #proctext(procedure_text) values( @cmd ) select @cmd = N' if (@fpkeyupdated = -1) return -1' insert into #proctext(procedure_text) values( @cmd ) -- -- script non PK unique key update check -- if (@fhasnonpkuniquekeys = 1) begin select @cmd = N' exec @fnpukeyupdated = dbo.sp_MSisnonpkukupdateinconflict ' + cast(@pubid as nvarchar(10)) + N', ' + cast(@artid as nvarchar(10)) + N', @bitmap' insert into #proctext(procedure_text) values( @cmd ) select @cmd = N' if (@fnpukeyupdated = -1) return -1' insert into #proctext(procedure_text) values( @cmd ) end -- -- continue scripting -- select @cmd = N' if (@rowcount = 0) begin ' + N'-- ' + N'-- we had conflict for this command ' + N'--' insert into #proctext(procedure_text) values( @cmd ) if (@fhasnonpkuniquekeys = 1) begin select @cmd = N' if (@error in (547, 2601, 2627) or (@fpkeyupdated = 1) or (@fnpukeyupdated = 1)) ' end else begin select @cmd = N' if (@error in (547, 2601, 2627) or (@fpkeyupdated = 1)) ' end insert into #proctext(procedure_text) values( @cmd ) -- -- continue scripting -- select @cmd = N' begin ' + N'-- ' + N'-- Conflict due to unique key/constraint ' + N'--' insert into #proctext(procedure_text) values( @cmd ) select @cmd = N' if (@fpkeyupdated = 1) begin ' + N'-- ' + N'-- PK is being updated ' + N'--' insert into #proctext(procedure_text) values( @cmd ) -- -- script check for rows with all keys with OLD values -- --if (row exists with pk = OLD_PK or non PK unique keys = OLD values) select @cmd = N' if exists (select * from ' + @qualname insert into #proctext(procedure_text) values( @cmd ) exec @rc = sp_replscriptuniquekeywhereclause @tabid = @objid ,@columns = @columns ,@prefix = N'@c' ,@suffix = N'_old' ,@mode = 6 insert into #proctext(procedure_text) values( N' )') -- -- continue scripting -- select @cmd = N' begin ' + N'-- ' + N'-- case 14: row(s) with OLD key values exist(s) ' + N'-- (and rows with NEW key values do not exist) ' + N'-- PubWins ----------------------------------------------------------- ' + N'-- generate delete + insert compensating action with OLD values for all unique keys ' + N'-- generate delete compensating action for row with PK = NEW_PK ' + N'-- SubWins ----------------------------------------------------------- ' + N'-- delete row with PK=OLD_PK ' + N'-- insert row with NEW values (use bitmap) ' + N'-- select @cftcase = 14 end' insert into #proctext(procedure_text) values( @cmd ) -- -- script check for rows with all keys with NEW values (use bitmap) -- --if (row exists with pk = NEW_PK or non PK unique keys = NEW values) select @cmd = N' if exists (select * from ' + @qualname insert into #proctext(procedure_text) values( @cmd ) exec @rc = sp_replscriptuniquekeywhereclause @tabid = @objid ,@columns = @columns ,@prefix = N'@c' ,@suffix = NULL ,@mode = 6 insert into #proctext(procedure_text) values( N' )') -- -- continue scripting -- select @cmd = N' begin ' + N'-- ' + N'-- row with NEW key values exist(s) ' + N'-- if (@cftcase = 14) begin ' + N'-- ' + N'-- case 15: rows exist with NEW key values and OLD key values ' + N'-- PubWins ----------------------------------------------------------- ' + N'-- generate delete + insert compensating action with OLD values for all unique keys ' + N'-- generate delete + insert compensating action with NEW values for all unique keys ' + N'-- SubWins ----------------------------------------------------------- ' + N'-- delete row with PK=OLD_PK ' + N'-- delete rows with NEW values of all keys ' + N'-- insert row with NEW values (use bitmap) ' + N'-- select @cftcase = 15 end' insert into #proctext(procedure_text) values( @cmd ) select @cmd = N' else begin ' + N'-- ' + N'-- case 16: rows exist with NEW key values and ' + N'-- row does not exist for OLD values ' + N'-- PubWins ----------------------------------------------------------- ' + N'-- generate delete compensating action for row with PK = OLD_PK ' + N'-- generate delete + insert compensating action with NEW values for all unique keys ' + N'-- SubWins ----------------------------------------------------------- ' + N'-- delete rows with NEW values of all keys ' + N'-- insert row with NEW values (use bitmap) ' + N'-- select @cftcase = 16 end end' insert into #proctext(procedure_text) values( @cmd ) select @cmd = N' else begin ' + N'-- ' + N'-- row with NEW key values does not exist ' + N'-- if (@cftcase = 0) begin ' + N'-- ' + N'-- case 12 : no existing rows with OLD key values or NEW or new key values ' + N'-- PubWins ----------------------------------------------------------- ' + N'-- generate delete compensating action with PK = OLD_PK ' + N'-- generate delete compensating action with PK = NEW_PK ' + N'-- SubWins ----------------------------------------------------------- ' + N'-- insert row with NEW values (use bitmap) ' + N'-- select @cftcase = 12 end' insert into #proctext(procedure_text) values( @cmd ) select @cmd = N' else begin ' + N'-- ' + N'-- case 14: row(s) with OLD key values exist(s) ' + N'-- (and rows with NEW key values do not exist) ' + N'-- PubWins ----------------------------------------------------------- ' + N'-- generate delete + insert compensating action with OLD values for all unique keys ' + N'-- generate delete compensating action for row with PK = NEW_PK ' + N'-- SubWins ----------------------------------------------------------- ' + N'-- delete row with PK=OLD_PK ' + N'-- insert row with NEW values (use bitmap) ' + N'-- select @cftcase = 14 end end end' insert into #proctext(procedure_text) values( @cmd ) -- -- script this block if the article has non PK unique keys -- if (@fhasnonpkuniquekeys = 1) begin -- -- continue scripting -- select @cmd = N' else if (@fnpukeyupdated = 1) begin '+N'-- '+N'-- non PK unique keys are being updated but PK is not updated '+N'-- OLD_PK == NEW_PK in these cases '+N'--' insert into #proctext(procedure_text) values( @cmd ) -- -- script the pkrowexists assignment -- select @cmd = N' declare @pkrowexist bit if exists (select * from ' + @qualname insert into #proctext(procedure_text) values( @cmd ) exec dbo.sp_MSscript_where_clause @objid, @columns, 'upd version', NULL, 0 select @cmd = N' ) select @pkrowexist = 1 ' insert into #proctext(procedure_text) values( @cmd ) -- -- script check for rows with non PK keys with OLD values -- -- if (rows exist with OLD values of non PK unique keys values) select @cmd = N' if exists (select * from ' + @qualname insert into #proctext(procedure_text) values( @cmd ) exec @rc = sp_replscriptuniquekeywhereclause @tabid = @objid ,@columns = @columns ,@prefix = N'@c' ,@suffix = N'_old' ,@mode = 7 insert into #proctext(procedure_text) values( N' )') -- -- continue scripting -- select @cmd = N' begin if (@pkrowexist = 1) begin '+N'-- '+N'-- case 10: rows exist with OLD non PK key values '+N'-- (and rows with NEW non PK key values do not exist) '+N'-- and row with OLD_PK exists '+N'-- PubWins ----------------------------------------------------------- '+N'-- generate delete + insert compensation action with OLD values for all keys '+N'-- generate delete compensating action with NEW values for non PK keys '+N'-- SubWins ----------------------------------------------------------- '+N'-- delete row with PK=OLD_PK '+N'-- insert row with NEW values (use bitmap) '+N'-- select @cftcase = 10 end' insert into #proctext(procedure_text) values( @cmd ) select @cmd = N' else begin '+N'-- '+N'-- case 21: rows exist with OLD non PK key values '+N'-- (and rows with NEW non PK key values do not exist) '+N'-- and row with OLD_PK does not exist '+N'-- PubWins ----------------------------------------------------------- '+N'-- generate delete with PK = OLD_PK '+N'-- generate delete + insert compensation action with OLD values for non PK keys '+N'-- generate delete compensating action with NEW values for non PK keys '+N'-- SubWins ----------------------------------------------------------- '+N'-- delete row with PK=OLD_PK '+N'-- insert row with NEW values (use bitmap) '+N'-- select @cftcase = 21 end end ' insert into #proctext(procedure_text) values( @cmd ) -- -- script check for rows with non PK keys with NEW values (use bitmap) -- -- if (rows exist with NEW values of non PK unique keys values) select @cmd = N' if exists (select * from ' + @qualname insert into #proctext(procedure_text) values( @cmd ) exec @rc = sp_replscriptuniquekeywhereclause @tabid = @objid ,@columns = @columns ,@prefix = N'@c' ,@suffix = NULL ,@mode = 7 insert into #proctext(procedure_text) values( N' )') -- -- continue scripting -- select @cmd = N' begin '+N'-- '+N'-- find the type of conflict '+N'-- if (@cftcase in (10,21)) begin if (@pkrowexist = 1) begin '+N'-- '+N'-- case 20: rows exist with OLD and NEW values of non PK keys '+N'-- and row with OLD_PK exists '+N'-- PubWins ----------------------------------------------------------- '+N'-- generate delete + insert compensation action with OLD values for all keys '+N'-- generate delete + insert compensation action with NEW values for non PK keys '+N'-- SubWins ----------------------------------------------------------- '+N'-- delete row with PK=OLD_PK '+N'-- delete row with NEW values for non PK keys '+N'-- insert row with NEW values (use bitmap) '+N'-- select @cftcase = 20 end ' insert into #proctext(procedure_text) values( @cmd ) select @cmd = N' else begin '+N'-- '+N'-- case 22: rows exist with OLD and NEW values of non PK keys '+N'-- and row with OLD_PK does not exist '+N'-- PubWins ----------------------------------------------------------- '+N'-- generate delete with PK = OLD_PK '+N'-- generate delete + insert compensation action with OLD values for non PK keys '+N'-- generate delete + insert compensation action with NEW values for non PK keys '+N'-- SubWins ----------------------------------------------------------- '+N'-- delete row with PK=OLD_PK '+N'-- delete row with NEW values for non PK keys '+N'-- insert row with NEW values (use bitmap) '+N'-- select @cftcase = 22 end end ' insert into #proctext(procedure_text) values( @cmd ) select @cmd = N' else begin if (@pkrowexist = 1) begin '+N'-- '+N'-- case 17: rows exist with NEW values of non PK keys '+N'-- and row does not exist with OLD values of non PK keys '+N'-- and row with OLD_PK exists '+N'-- PubWins ----------------------------------------------------------- '+N'-- generate delete compensating action with OLD values for non PK keys '+N'-- generate delete + insert compensation action with NEW values for all keys '+N'-- SubWins ----------------------------------------------------------- '+N'-- delete row with NEW values for non PK keys '+N'-- insert row with NEW values (use bitmap) '+N'-- select @cftcase = 17 end ' insert into #proctext(procedure_text) values( @cmd ) select @cmd = N' else begin '+N'-- '+N'-- case 23: rows exist with NEW values of non PK keys '+N'-- and row does not exist with OLD values of non PK keys '+N'-- and row with OLD_PK does not exist '+N'-- PubWins ----------------------------------------------------------- '+N'-- delete row with PK=OLD_PK '+N'-- generate delete compensating action with OLD values for non PK keys '+N'-- generate delete + insert compensation action with NEW values for non PK keys '+N'-- SubWins ----------------------------------------------------------- '+N'-- delete row with NEW values for non PK keys '+N'-- insert row with NEW values (use bitmap) '+N'-- select @cftcase = 23 end end end ' insert into #proctext(procedure_text) values( @cmd ) select @cmd = N' else begin '+N'-- '+N'-- row does not exist with NEW values of non PK keys '+N'-- if (@cftcase = 0) begin if (@pkrowexist = 1) begin '+N'-- '+N'-- case 18 : no existing rows with OLD or NEW values of non PK keys '+N'-- and row with OLD_PK exists '+N'-- PubWins ----------------------------------------------------------- '+N'-- delete row with PK=OLD_PK '+N'-- generate delete compensating action with OLD values for non PK keys '+N'-- generate delete compensating action with NEW values for non PK keys '+N'-- generate insert with PK = OLD_PK '+N'-- SubWins ----------------------------------------------------------- '+N'-- insert row with NEW values (use bitmap) '+N'-- select @cftcase = 18 end ' insert into #proctext(procedure_text) values( @cmd ) select @cmd = N' else begin '+N'-- '+N'-- case 24 : no existing rows with OLD or NEW values of non PK keys '+N'-- and row with OLD_PK does not exist '+N'-- PubWins ----------------------------------------------------------- '+N'-- delete row with PK=OLD_PK '+N'-- generate delete compensating action with OLD values for non PK keys '+N'-- generate delete compensating action with NEW values for non PK keys '+N'-- SubWins ----------------------------------------------------------- '+N'-- insert row with NEW values (use bitmap) '+N'-- select @cftcase = 24 end end end end ' insert into #proctext(procedure_text) values( @cmd ) end -- -- continue scripting -- select @cmd = N' end else begin ' + N'-- ' + N'-- Conflict due non key column change or row deleted ' + N'--' insert into #proctext(procedure_text) values( @cmd ) -- -- script check for rows with pk = OLD_PK -- select @cmd = N' if exists (select * from ' + @qualname insert into #proctext(procedure_text) values( @cmd ) exec dbo.sp_MSscript_where_clause @objid, @columns, 'upd version', NULL, 0 insert into #proctext(procedure_text) values( N' )') -- -- continue scripting -- select @cmd = N' begin ' + N'-- ' + N'-- case 11: row exists ' + N'-- PubWins ----------------------------------------------------------- ' + N'-- generate delete + insert compensating action with PK = OLD_PK ' + N'-- SubWins ----------------------------------------------------------- ' + N'-- delete row with PK=OLD_PK ' + N'-- insert row with NEW values (use bitmap) ' + N'-- select @cftcase = 11 end' insert into #proctext(procedure_text) values( @cmd ) select @cmd = N' else begin ' + N'-- ' + N'-- case 13: row does not exist ' + N'-- PubWins ----------------------------------------------------------- ' + N'-- generate delete compensating action with PK = OLD_PK ' + N'-- SubWins ----------------------------------------------------------- ' + N'-- insert row with NEW values (use bitmap) ' + N'-- select @cftcase = 13 end end end' insert into #proctext(procedure_text) values( @cmd ) select @cmd = N' else if (@execution_mode = @QPubWins) begin ' + N'-- ' + N'-- we had no conflict for this command ' + N'-- We need to process this block only in the Publisher Wins cases ' + N'--' insert into #proctext(procedure_text) values( @cmd ) select @cmd = N' if (@fpkeyupdated = 1) begin ' + N'-- ' + N'-- PK is being updated ' + N'-- PubWins ----------------------------------------------------------- ' + N'-- generate delete + insert compensating action with OLD values for all unique keys ' + N'-- generate delete compensating action with PK=NEW_PK ' + N'-- select @cftcase = 1 end' insert into #proctext(procedure_text) values( @cmd ) select @cmd = N' else begin ' + N'-- ' + N'-- non PK column updated ' + N'-- PubWins ----------------------------------------------------------- ' + N'-- generate delete + insert compensating action with OLD values for all unique keys ' + N'-- select @cftcase = 3 end end end' insert into #proctext(procedure_text) values( @cmd ) -- -- all done -- return 0 end go exec dbo.sp_MS_marksystemobject sp_MSscriptupdateconflictfinder go -------------------------------------------------------------------------------- --. sp_MSscript_update_statement -------------------------------------------------------------------------------- if exists (select * from sysobjects where type = 'P' and name = 'sp_MSscript_update_statement') drop procedure sp_MSscript_update_statement go raiserror('Creating procedure sp_MSscript_update_statement', 0,1) go create procedure sp_MSscript_update_statement ( @publication sysname, @article sysname, @objid int, @columns binary(32), @queued_pub bit = 0 ) as BEGIN declare @cmd nvarchar(4000) ,@cmd2 nvarchar(4000) ,@qualname nvarchar(512) ,@colname sysname ,@typestring nvarchar(4000) ,@spacer nvarchar(1) ,@ccoltype sysname ,@this_col int ,@rc int ,@column nvarchar(4000) ,@num_col int ,@bitstr nvarchar(20) ,@bytestr nvarchar(20) ,@art_col int -- position in the article partition. ,@isset int ,@timestamp_subscribed bit ,@pubid int select @pubid = pubid from syspublications where name = @publication if exists (select * from sysarticles where pubid = @pubid and name = @article and status & 32 <> 0) select @timestamp_subscribed = 1 else select @timestamp_subscribed = 0 -- -- Start scripting -- select @cmd = N' ' + N'-- ' + N'-- detection/conflict resolution stage ' + N'--' if (@queued_pub = 1) begin select @cmd = @cmd + N' if (@execution_mode = @QPubWins) save tran cftpass ' end insert into #proctext(procedure_text) values(@cmd) -- -- Generate the update statement -- exec sp_MSget_qualified_name @objid, @qualname OUTPUT select @cmd2 = N' update ' + @qualname + N' set ' select @spacer = N' ' select @cmd = N'' exec dbo.sp_MSpad_command @cmd output, 8 select @num_col = 0 select @art_col = 0 DECLARE hCColid CURSOR LOCAL FAST_FORWARD FOR select colid from syscolumns where id = @objid order by colid asc OPEN hCColid FETCH hCColid INTO @this_col WHILE (@@fetch_status <> -1) begin -- Get the ordinal of the article partition or not. exec @isset = dbo.sp_isarticlecolbitset @this_col, @columns if @isset = 0 begin -- Special handling of a timestamp col in a queued tran publication -- See sp_helparticlecolumns : xtype 189 is timestamp if ((@timestamp_subscribed = 1) and exists ( select * from dbo.syscolumns where id = @objid and colid = @this_col and xtype = 189)) select @art_col = @art_col + 1 end else select @art_col = @art_col + 1 exec @rc = dbo.sp_MSget_colinfo @objid, @this_col, @columns, 0, @colname output, @ccoltype output if @rc = 0 and EXISTS (select name from syscolumns where id=@objid and colid=@this_col and iscomputed<>1) begin if rtrim(@ccoltype) not like N'timestamp' and ColumnProperty(@objid, @colname, 'IsIdentity') != 1 begin if @cmd2 is not null begin exec dbo.sp_MSflush_command @cmd2 output, 1, 8 select @cmd2 = null end select @num_col = @num_col + 1 -- Optimization: -- Get null or actual column name -- Note: the output is quoted. exec dbo.sp_MSget_synctran_column @ts_col = null, @op_type = null , -- 'ins, 'upd', 'del' @is_new = null, @primary_key_bitmap = null, @colname = @colname, @this_col = @this_col, @column = @column output, @from_proc = 1, @art_col = @art_col -- position in the partition. select @cmd = @cmd + @spacer + QUOTENAME(@colname) + N' = ' + @column select @spacer = N',' -- -- flush command if necessary -- exec dbo.sp_MSflush_command @cmd output, 1, 8 end end FETCH hCColid INTO @this_col end CLOSE hCColid DEALLOCATE hCColid -- save off cmd fragment if @num_col > 0 begin -- -- Add the where clause based on the update mode -- select @colname = 'msrepl_tran_version' exec dbo.sp_MSscript_where_clause @objid, @columns, 'upd version', @colname, 4 end else -- set the @@rowcount insert into #proctext(procedure_text) values( N' select @retcode = @retcode ') -- -- continue with rest of scripting -- select @cmd = N' select @rowcount = @@ROWCOUNT, @error = @@ERROR ' if (@queued_pub = 1) begin select @cmd = @cmd + N' if (@execution_mode = @QPubWins) rollback tran cftpass' end insert into #proctext(procedure_text) values(@cmd) -- -- Queued specific case -- if (@queued_pub = 1) begin -- -- script the assignment of new values based on bitmask -- select @num_col = 0 ,@art_col = 0 ,@cmd = N' ' + N'-- ' + N'-- for conflict resolution - assign the NEW values based on bitmap ' + N'-- if (@execution_mode in (@QPubWins, @QSubWins)) begin select ' insert into #proctext(procedure_text) values(@cmd) DECLARE hCColid CURSOR LOCAL FAST_FORWARD FOR select colid from dbo.syscolumns where id = @objid order by colid asc OPEN hCColid FETCH hCColid INTO @this_col WHILE (@@fetch_status != -1) begin -- Get the ordinal of the article partition or not. exec @isset = dbo.sp_isarticlecolbitset @this_col, @columns if @isset = 0 begin if ((@timestamp_subscribed = 1) and exists ( select * from dbo.syscolumns where id = @objid and colid = @this_col and xtype = 189)) select @art_col = @art_col + 1 end else select @art_col = @art_col + 1 exec @rc = dbo.sp_MSget_colinfo @objid, @this_col, @columns, 0, @colname output, @ccoltype output if ((@rc = 0) and EXISTS (select name from dbo.syscolumns where id=@objid and colid=@this_col and iscomputed != 1) and (rtrim(@ccoltype) != N'timestamp')) begin select @num_col = @num_col + 1 ,@bytestr = cast((1 + (@art_col-1) / 8 ) as nvarchar) ,@bitstr = cast( power(2, (@art_col-1) % 8 ) as nvarchar) select @cmd = N'@c' + cast(@this_col as nvarchar(10)) + N' = case substring(@bitmap,' + @bytestr + N',1) & ' + @bitstr + N' when ' + @bitstr + N' then @c' + cast(@this_col as nvarchar(4)) + N' else @c' + cast(@this_col as nvarchar(4)) + N'_old end ' if (@num_col = 1) begin select @cmd = N' ' + @cmd end else begin select @cmd = N' , ' + @cmd end insert into #proctext(procedure_text) values(@cmd) end -- -- fetch next row -- FETCH hCColid INTO @this_col end CLOSE hCColid DEALLOCATE hCColid -- -- continue with scripting -- select @cmd = N' end' insert into #proctext(procedure_text) values(@cmd) end -- -- all done -- return 0 END go exec dbo.sp_MS_marksystemobject sp_MSscript_update_statement GO -------------------------------------------------------------------------------- --. sp_MSscript_insert_subwins -------------------------------------------------------------------------------- if exists (select * from sysobjects where type = 'P' and name = 'sp_MSscript_insert_subwins') drop procedure sp_MSscript_insert_subwins go raiserror('Creating procedure sp_MSscript_insert_subwins', 0,1) go create procedure sp_MSscript_insert_subwins ( @publication sysname -- publication name ,@article sysname -- article name ,@objid int -- object id ,@columns binary(32) -- columns replicated ,@identity_insert bit -- enable identity insert ) as begin declare @cmd nvarchar(4000) ,@qualname nvarchar(512) ,@rc int ,@fhasnonpkuniquekeys int exec sp_MSget_qualified_name @objid, @qualname OUTPUT -- -- check if this article has non PK unique keys -- exec @fhasnonpkuniquekeys = dbo.sp_repltablehasnonpkuniquekey @tabid = @objid -- -- start scripting -- select @cmd = N' if (@execution_mode = @QSubWins) begin '+N'-- '+N'-- Subscriber wins resolution '+N'-- ' insert into #proctext(procedure_text) values( @cmd ) -- -- script this block if the article has non PK unique keys -- if (@fhasnonpkuniquekeys = 1) begin select @cmd = N' if (@cftcase = 22) begin '+N'-- '+N'-- delete rows with values of non PK keys '+N'-- ' insert into #proctext(procedure_text) values( @cmd ) -- -- script delete rows with values for non PK keys -- select @cmd = N' delete ' + @qualname insert into #proctext(procedure_text) values( @cmd ) exec @rc = sp_replscriptuniquekeywhereclause @tabid = @objid ,@columns = @columns ,@prefix = N'@c' ,@suffix = NULL ,@mode = 7 -- -- continue scripting -- select @cmd = N' end' insert into #proctext(procedure_text) values( @cmd ) end -- -- continue scripting -- select @cmd = N' if (@cftcase = 21) begin '+N'-- '+N'-- delete rows with values of all keys '+N'-- ' insert into #proctext(procedure_text) values( @cmd ) -- -- script delete rows with values of all keys -- select @cmd = N' delete ' + @qualname insert into #proctext(procedure_text) values( @cmd ) exec @rc = sp_replscriptuniquekeywhereclause @tabid = @objid ,@columns = @columns ,@prefix = N'@c' ,@suffix = NULL ,@mode = 6 -- -- continue scripting -- select @cmd = N' end if (@cftcase in (21,22)) begin '+N'-- '+N'-- insert row '+N'-- ' insert into #proctext(procedure_text) values( @cmd ) -- -- script insert row -- exec @rc = sp_script_insertforcftresolution @objid = @objid ,@columns = @columns ,@identity_insert = @identity_insert ,@prefix = N'@c' ,@suffix = NULL -- -- continue scripting -- select @cmd = N' end '+N'-- '+N'-- -------------------------------------------------------------------- '+N'-- all done for conflict resolution for Subscriber Wins policy '+N'-- -------------------------------------------------------------------- '+N'-- end' insert into #proctext(procedure_text) values( @cmd ) -- -- all done -- return 0 end go exec dbo.sp_MS_marksystemobject sp_MSscript_insert_subwins go -------------------------------------------------------------------------------- --. sp_MSscript_insert_pubwins -------------------------------------------------------------------------------- if exists (select * from sysobjects where type = 'P' and name = 'sp_MSscript_insert_pubwins') drop procedure sp_MSscript_insert_pubwins go raiserror('Creating procedure sp_MSscript_insert_pubwins', 0,1) go create procedure sp_MSscript_insert_pubwins ( @publication sysname -- publication name ,@article sysname -- article name ,@objid int -- object id ,@columns binary(32) -- columns replicated ) as begin declare @cmd nvarchar(4000) ,@artid int ,@pubid int ,@dest_table sysname ,@dest_owner nvarchar(260) ,@rc int ,@qualname nvarchar(512) ,@fhasnonpkuniquekeys int -- -- initialize the vars we will use -- select @pubid = pubid from syspublications where name = @publication select @artid = artid, @dest_table = dest_table, @dest_owner = dest_owner from sysarticles where name = @article and pubid = @pubid select @dest_owner = case when (@dest_owner IS NULL) then N'' else quotename(@dest_owner) + N'.' end exec sp_MSget_qualified_name @objid, @qualname OUTPUT -- -- check if this article has non PK unique keys -- exec @fhasnonpkuniquekeys = dbo.sp_repltablehasnonpkuniquekey @tabid = @objid -- -- start scripting -- select @cmd = N' if (@execution_mode = @QPubWins) begin '+N'-- '+N'-- Publisher wins resolution '+N'-- ' insert into #proctext(procedure_text) values( @cmd ) -- -- declare fetch variables for cursor -- exec @rc = sp_scriptpubwinsrefreshcursorvars @objid -- -- continue scripting -- select @cmd = N' '+N'-- '+N'-- -------------------------------------------------------------------- '+N'-- Perform single row delete generations first '+N'-- -------------------------------------------------------------------- '+N'--' insert into #proctext(procedure_text) values( @cmd ) -- -- continue scripting -- select @cmd = N' '+N'-- '+N'-- Generate DELETE for PK '+N'-- if (@cftcase in (21,22,23)) begin' insert into #proctext(procedure_text) values( @cmd ) -- -- Generate the delete compensating code -- select @cmd = N' select @cmd = ''DELETE ' + master.dbo.fn_MSgensqescstr(@dest_owner) collate database_default + quotename(master.dbo.fn_MSgensqescstr(@dest_table) collate database_default) + N' '' + ' insert into #proctext(procedure_text) values( @cmd ) exec dbo.sp_MSscript_where_clause @objid, @columns, 'qcft_comp', NULL, 0, 'ins' -- -- generate the send command -- exec sp_MSscript_compensating_send @pubid, @artid, 0, 1 -- -- continue scripting -- select @cmd = N' end '+N'-- '+N'-- -------------------------------------------------------------------- '+N'-- Perform refresh(delete+insert) generations next '+N'-- -------------------------------------------------------------------- '+N'--' insert into #proctext(procedure_text) values( @cmd ) -- -- script this block if the article has non PK unique keys -- if (@fhasnonpkuniquekeys = 1) begin select @cmd = N' '+N'-- '+N'-- Generate delete+insert for values of non PK keys '+N'-- if (@cftcase = 22) begin' insert into #proctext(procedure_text) values( @cmd ) -- -- generate delete compensating command for values of non PK unique keys -- select @cmd = N' select @cmd = ''DELETE ' + master.dbo.fn_MSgensqescstr(@dest_owner) collate database_default + quotename(master.dbo.fn_MSgensqescstr(@dest_table) collate database_default) + N' '' + ' insert into #proctext(procedure_text) values( @cmd ) exec @rc = sp_replscriptuniquekeywhereclause @tabid = @objid ,@columns = @columns ,@prefix = N'@c' ,@suffix = NULL ,@mode = 5 -- -- script the sending command -- exec sp_MSscript_compensating_send @pubid, @artid, 0, 1 -- -- script the refresh commands for values of non PK unique keys -- exec @rc = dbo.sp_MSscript_compensating_insert @publication, @article, @objid, @columns, 3, 0 if (@rc != 0 or @@error != 0) return 1 -- -- continue scripting -- select @cmd = N' end' insert into #proctext(procedure_text) values( @cmd ) end -- -- continue scripting -- select @cmd = N' '+N'-- '+N'-- Generate delete+insert for values of all keys '+N'-- if (@cftcase = 21) begin' insert into #proctext(procedure_text) values( @cmd ) -- -- generate delete compensating command for values of all unique keys -- select @cmd = N' select @cmd = ''DELETE ' + master.dbo.fn_MSgensqescstr(@dest_owner) collate database_default + quotename(master.dbo.fn_MSgensqescstr(@dest_table) collate database_default) + N' '' + ' insert into #proctext(procedure_text) values( @cmd ) exec @rc = sp_replscriptuniquekeywhereclause @tabid = @objid ,@columns = @columns ,@prefix = N'@c' ,@suffix = NULL ,@mode = 4 -- -- script the sending command -- exec sp_MSscript_compensating_send @pubid, @artid, 0, 1 -- -- script the refresh commands for values of all unique keys -- exec @rc = dbo.sp_MSscript_compensating_insert @publication, @article, @objid, @columns, 5, 0 if (@rc != 0 or @@error != 0) return 1 -- -- continue scripting -- select @cmd = N' end '+N'-- '+N'-- -------------------------------------------------------------------- '+N'-- all done for conflict resolution for Publisher Wins policy '+N'-- -------------------------------------------------------------------- '+N'-- end' insert into #proctext(procedure_text) values( @cmd ) -- -- all done -- return 0 end go exec dbo.sp_MS_marksystemobject sp_MSscript_insert_pubwins go -------------------------------------------------------------------------------- --. sp_MSscript_update_subwins -------------------------------------------------------------------------------- if exists (select * from sysobjects where type = 'P' and name = 'sp_MSscript_update_subwins') drop procedure sp_MSscript_update_subwins go raiserror('Creating procedure sp_MSscript_update_subwins', 0,1) go create procedure sp_MSscript_update_subwins ( @publication sysname -- publication name ,@article sysname -- article name ,@objid int -- object id ,@columns binary(32) -- columns replicated ,@identity_insert bit ) AS BEGIN declare @cmd nvarchar(4000) ,@artid int ,@pubid int ,@qualname nvarchar(512) ,@rc int ,@fhasnonpkuniquekeys int -- -- initialize the vars we will use -- exec sp_MSget_qualified_name @objid, @qualname OUTPUT -- -- check if this article has non PK unique keys -- exec @fhasnonpkuniquekeys = dbo.sp_repltablehasnonpkuniquekey @tabid = @objid -- -- start scripting -- select @cmd = N' if (@execution_mode = @QSubWins) begin ' + N'-- ' + N'-- Subscriber wins resolution ' + N'-- if (@cftcase in (10,11,14,15,18,20,21,22)) begin '+N'-- '+N'-- delete the row with OLD_PK '+N'--' insert into #proctext(procedure_text) values( @cmd ) -- -- script delete with PK=OLD_PK -- select @cmd = N' delete ' + @qualname insert into #proctext(procedure_text) values( @cmd ) exec dbo.sp_MSscript_where_clause @objid, @columns, 'upd version', NULL, 0 -- -- continue scripting -- select @cmd = N' end' insert into #proctext(procedure_text) values( @cmd ) -- -- if the table has non PK unique keys -- generate the commands for non PK keys -- if (@fhasnonpkuniquekeys = 1) begin select @cmd = N' if (@cftcase in (17,20,22,23)) begin '+N'-- '+N'-- delete with NEW values of non PK keys '+N'--' insert into #proctext(procedure_text) values( @cmd ) -- -- script delete with NEW values of non PK keys -- select @cmd = N' delete ' + @qualname insert into #proctext(procedure_text) values( @cmd ) exec @rc = sp_replscriptuniquekeywhereclause @tabid = @objid ,@columns = @columns ,@prefix = N'@c' ,@suffix = NULL ,@mode = 7 -- -- continue scripting -- select @cmd = N' end' insert into #proctext(procedure_text) values( @cmd ) end -- -- continue scripting -- select @cmd = N' if (@cftcase in (15,16)) begin '+N'-- '+N'-- delete with NEW values of all keys '+N'--' insert into #proctext(procedure_text) values( @cmd ) -- -- script delete with NEW values of all keys -- select @cmd = N' delete ' + @qualname insert into #proctext(procedure_text) values( @cmd ) exec @rc = sp_replscriptuniquekeywhereclause @tabid = @objid ,@columns = @columns ,@prefix = N'@c' ,@suffix = NULL ,@mode = 6 -- -- continue scripting -- select @cmd = N' end if (@cftcase in (10,11,12,13,14,15,16,17,18,20,21,22,23,24)) begin '+N'-- '+N'-- insert with NEW values of all keys '+N'--' insert into #proctext(procedure_text) values( @cmd ) -- -- -- generate insert statement with NEW values -- exec @rc = sp_script_insertforcftresolution @objid = @objid ,@columns = @columns ,@identity_insert = @identity_insert ,@prefix = N'@c' ,@suffix = NULL -- -- continue scripting -- select @cmd = N' end ' + N'-- ' + N'-- -------------------------------------------------------------------- ' + N'-- all done for conflict resolution for Subscriber Wins policy ' + N'-- -------------------------------------------------------------------- ' + N'-- end' insert into #proctext(procedure_text) values( @cmd ) -- -- all done -- return 0 END go exec dbo.sp_MS_marksystemobject sp_MSscript_update_subwins go -------------------------------------------------------------------------------- --. sp_MSscript_update_pubwins -------------------------------------------------------------------------------- if exists (select * from sysobjects where type = 'P' and name = 'sp_MSscript_update_pubwins') drop procedure sp_MSscript_update_pubwins go raiserror('Creating procedure sp_MSscript_update_pubwins', 0,1) go create procedure sp_MSscript_update_pubwins ( @publication sysname -- publication name ,@article sysname -- article name ,@objid int -- object id ,@columns binary(32) -- columns replicated ) as begin declare @cmd nvarchar(4000) ,@artid int ,@pubid int ,@qualname nvarchar(512) ,@dest_table sysname ,@dest_owner nvarchar(260) ,@rc int ,@fhasnonpkuniquekeys int -- -- initialize the vars we will use -- select @pubid = pubid from syspublications where name = @publication select @artid = artid, @dest_table = dest_table, @dest_owner = dest_owner from sysarticles where name = @article and pubid = @pubid select @dest_owner = case when (@dest_owner IS NULL) then N'' else quotename(@dest_owner) + N'.' end exec sp_MSget_qualified_name @objid, @qualname OUTPUT -- -- check if this article has non PK unique keys -- exec @fhasnonpkuniquekeys = dbo.sp_repltablehasnonpkuniquekey @tabid = @objid -- -- start scripting -- select @cmd = N' ' + N'-- ' + N'-- -------------------------------------------------------------------- ' + N'-- Now the generation phase ' + N'-- Use the conflict case value to decide what to do ' + N'-- -------------------------------------------------------------------- ' + N'-- ' insert into #proctext(procedure_text) values( @cmd ) select @cmd = N' if (@execution_mode = @QPubWins) begin ' + N'-- ' + N'-- Publisher wins resolution ' + N'-- ' insert into #proctext(procedure_text) values( @cmd ) -- -- declare fetch variables for cursor -- exec @rc = sp_scriptpubwinsrefreshcursorvars @objid -- -- continue scripting -- select @cmd = N' ' + N'-- ' + N'-- -------------------------------------------------------------------- ' + N'-- Perform single row delete generations first ' + N'-- -------------------------------------------------------------------- ' + N'-- ' insert into #proctext(procedure_text) values( @cmd ) select @cmd = N' ' + N'-- ' + N'-- Generate DELETE for PK = OLD_PK ' + N'-- if (@cftcase in (11,12,13,16,18,21,22,23,24)) begin' insert into #proctext(procedure_text) values( @cmd ) -- -- generate delete compensating cmd with OLD_PK -- select @cmd = N' select @cmd = ''DELETE ' + master.dbo.fn_MSgensqescstr(@dest_owner) collate database_default + quotename(master.dbo.fn_MSgensqescstr(@dest_table) collate database_default) + N' '' + ' insert into #proctext(procedure_text) values( @cmd ) exec dbo.sp_MSscript_where_clause @objid, @columns, 'qcft_comp', NULL, 0, 'del' -- -- script the sending command -- exec sp_MSscript_compensating_send @pubid, @artid, 0, 1 -- -- continue scripting -- select @cmd = N' end ' + N'-- ' + N'-- Generate DELETE for PK = NEW_PK ' + N'-- if (@cftcase in (1,12,14)) begin' insert into #proctext(procedure_text) values( @cmd ) -- -- generate delete compensating cmd with NEW_PK -- select @cmd = N' select @cmd = ''DELETE ' + master.dbo.fn_MSgensqescstr(@dest_owner) collate database_default + quotename(master.dbo.fn_MSgensqescstr(@dest_table) collate database_default) + N' '' + ' insert into #proctext(procedure_text) values( @cmd ) exec dbo.sp_MSscript_where_clause @objid, @columns, 'qcft_comp', NULL, 0, 'ins' -- -- script the sending command -- exec sp_MSscript_compensating_send @pubid, @artid, 0, 1 -- -- continue scripting -- select @cmd = N' end' insert into #proctext(procedure_text) values( @cmd ) -- -- this scripting is specific to non PK unique keys -- if (@fhasnonpkuniquekeys = 1) begin -- -- continue scripting -- select @cmd = N' '+N'-- '+N'-- Generate delete for OLD values of non PK keys '+N'-- if (@cftcase in (17,18,23,24)) begin' insert into #proctext(procedure_text) values( @cmd ) -- -- generate delete compensating command for OLD values of non PK unique keys -- select @cmd = N' select @cmd = ''DELETE ' + master.dbo.fn_MSgensqescstr(@dest_owner) collate database_default + quotename(master.dbo.fn_MSgensqescstr(@dest_table) collate database_default) + N' '' + ' insert into #proctext(procedure_text) values( @cmd ) exec @rc = sp_replscriptuniquekeywhereclause @tabid = @objid ,@columns = @columns ,@prefix = N'@c' ,@suffix = N'_old' ,@mode = 5 -- -- script the sending command -- exec sp_MSscript_compensating_send @pubid, @artid, 0, 1 -- -- continue scripting -- select @cmd = N' end '+N'-- '+N'-- Generate delete for NEW values of non PK keys '+N'-- if (@cftcase in (10,18,21,24)) begin' insert into #proctext(procedure_text) values( @cmd ) -- -- generate delete compensating command for NEW values of non PK unique keys -- select @cmd = N' select @cmd = ''DELETE ' + master.dbo.fn_MSgensqescstr(@dest_owner) collate database_default + quotename(master.dbo.fn_MSgensqescstr(@dest_table) collate database_default) + N' '' + ' insert into #proctext(procedure_text) values( @cmd ) exec @rc = sp_replscriptuniquekeywhereclause @tabid = @objid ,@columns = @columns ,@prefix = N'@c' ,@suffix = NULL ,@mode = 5 -- -- script the sending command -- exec sp_MSscript_compensating_send @pubid, @artid, 0, 1 -- -- continue scripting -- select @cmd = N' end ' + N'-- ' + N'-- -------------------------------------------------------------------- ' + N'-- Perform refresh(delete+insert) generations next ' + N'-- -------------------------------------------------------------------- ' + N'-- ' + N'-- ' + N'-- Generate delete+insert for OLD values of non PK keys ' + N'-- if (@cftcase in (21,22)) begin' insert into #proctext(procedure_text) values( @cmd ) -- -- generate delete compensating command for OLD values of non PK unique keys -- select @cmd = N' select @cmd = ''DELETE ' + master.dbo.fn_MSgensqescstr(@dest_owner) collate database_default + quotename(master.dbo.fn_MSgensqescstr(@dest_table) collate database_default) + N' '' + ' insert into #proctext(procedure_text) values( @cmd ) exec @rc = sp_replscriptuniquekeywhereclause @tabid = @objid ,@columns = @columns ,@prefix = N'@c' ,@suffix = N'_old' ,@mode = 5 -- -- script the sending command -- exec sp_MSscript_compensating_send @pubid, @artid, 0, 1 -- -- script the refresh commands for OLD values of non PK unique keys -- exec @rc = dbo.sp_MSscript_compensating_insert @publication, @article, @objid, @columns, 2, 0 if (@rc != 0 or @@error != 0) return 1 -- -- continue scripting -- select @cmd = N' end ' + N'-- ' + N'-- Generate delete+insert for NEW values of non PK keys ' + N'-- if (@cftcase in (20,22,23)) begin' insert into #proctext(procedure_text) values( @cmd ) -- -- generate delete compensating command for NEW values of non PK unique keys -- select @cmd = N' select @cmd = ''DELETE ' + master.dbo.fn_MSgensqescstr(@dest_owner) collate database_default + quotename(master.dbo.fn_MSgensqescstr(@dest_table) collate database_default) + N' '' + ' insert into #proctext(procedure_text) values( @cmd ) exec @rc = sp_replscriptuniquekeywhereclause @tabid = @objid ,@columns = @columns ,@prefix = N'@c' ,@suffix = NULL ,@mode = 5 -- -- script the sending command -- exec sp_MSscript_compensating_send @pubid, @artid, 0, 1 -- -- script the refresh commands for NEW values of non PK unique keys -- exec @rc = dbo.sp_MSscript_compensating_insert @publication, @article, @objid, @columns, 3, 0 if (@rc != 0 or @@error != 0) return 1 -- -- continue scripting -- select @cmd = N' end' insert into #proctext(procedure_text) values( @cmd ) end -- -- continue scripting -- select @cmd = N' ' + N'-- ' + N'-- Generate delete+insert for OLD values of all keys ' + N'-- if (@cftcase in (1,3,10,14,15,20)) begin' insert into #proctext(procedure_text) values( @cmd ) -- -- generate delete compensating command for OLD values of all unique keys -- select @cmd = N' select @cmd = ''DELETE ' + master.dbo.fn_MSgensqescstr(@dest_owner) collate database_default + quotename(master.dbo.fn_MSgensqescstr(@dest_table) collate database_default) + N' '' + ' insert into #proctext(procedure_text) values( @cmd ) exec @rc = sp_replscriptuniquekeywhereclause @tabid = @objid ,@columns = @columns ,@prefix = N'@c' ,@suffix = N'_old' ,@mode = 4 -- -- script the sending command -- exec sp_MSscript_compensating_send @pubid, @artid, 0, 1 -- -- script the refresh commands for OLD values of all unique keys -- exec @rc = dbo.sp_MSscript_compensating_insert @publication, @article, @objid, @columns, 4, 0 if (@rc != 0 or @@error != 0) return 1 -- -- continue scripting -- select @cmd = N' end ' + N'-- ' + N'-- Generate delete+insert for NEW values of all keys ' + N'-- if (@cftcase in (15,16,17)) begin' insert into #proctext(procedure_text) values( @cmd ) -- -- generate delete compensating command for NEW values of all unique keys -- select @cmd = N' select @cmd = ''DELETE ' + master.dbo.fn_MSgensqescstr(@dest_owner) collate database_default + quotename(master.dbo.fn_MSgensqescstr(@dest_table) collate database_default) + N' '' + ' insert into #proctext(procedure_text) values( @cmd ) exec @rc = sp_replscriptuniquekeywhereclause @tabid = @objid ,@columns = @columns ,@prefix = N'@c' ,@suffix = NULL ,@mode = 4 -- -- script the sending command -- exec sp_MSscript_compensating_send @pubid, @artid, 0, 1 -- -- script the refresh commands for NEW values of all unique keys -- exec @rc = dbo.sp_MSscript_compensating_insert @publication, @article, @objid, @columns, 5, 0 if (@rc != 0 or @@error != 0) return 1 -- -- continue scripting -- select @cmd = N' end ' + N'-- ' + N'-- -------------------------------------------------------------------- ' + N'-- Perform single row insert generations next ' + N'-- -------------------------------------------------------------------- ' + N'-- ' + N'-- ' + N'-- Generate INSERT for PK = OLD_PK ' + N'-- if (@cftcase in (11,18)) begin' insert into #proctext(procedure_text) values( @cmd ) -- -- script compensating insert with OLD_PK -- exec dbo.sp_MSscript_compensating_insert @publication, @article, @objid, @columns, 1, 0 -- -- continue scripting -- select @cmd = N' end ' + N'-- ' + N'-- -------------------------------------------------------------------- ' + N'-- all done for conflict resolution for Publisher Wins policy ' + N'-- -------------------------------------------------------------------- ' + N'-- end' insert into #proctext(procedure_text) values( @cmd ) -- -- all done -- return 0 end go exec dbo.sp_MS_marksystemobject sp_MSscript_update_pubwins go -------------------------------------------------------------------------------- --. sp_MSscript_compensating_insert -------------------------------------------------------------------------------- if exists (select * from sysobjects where type = 'P' and name = 'sp_MSscript_compensating_insert') drop procedure sp_MSscript_compensating_insert go raiserror('Creating procedure sp_MSscript_compensating_insert', 0,1) go create procedure sp_MSscript_compensating_insert ( @publication sysname -- publication name ,@article sysname -- article name ,@objid int -- table object id ,@columns binary(32) -- columns replicated ,@proctype tinyint -- what are we scripting -- 0 = use new_pk -- 1 = use old_pk -- 2 = use old nonpkkeys -- 3 = use new nonpkkeys -- 4 = use oldallkeys -- 5 = use newallkeys ,@fdodeclare bit = 1 -- 0 = do not script declares for non PK unique key processing ) as begin declare @cmd nvarchar(4000) ,@artid int ,@pubid int ,@dest_table sysname ,@dest_owner nvarchar(260) ,@colname sysname ,@ccoltype sysname ,@this_col int ,@rc int ,@num_col int ,@qualname nvarchar(540) ,@cast_str nvarchar(4000) ,@column_string nvarchar(4000) ,@ins_cmd nvarchar(255) ,@startoffset int ,@setprefix bit ,@commandlen int ,@fragmentlen int ,@collen int ,@first_time bit ,@fullcastlen int ,@splitlen int ,@pkcolumns varbinary(32) ,@fhasnonpkuniquekeys int declare @pkfetch table ( c1 int identity NOT NULL, procedure_text nvarchar(4000) collate database_default null) -- -- constants -- declare @typeusenew_pk tinyint ,@typeuseold_pk tinyint ,@typeuseoldnonpkkeys tinyint ,@typeusenewnonpkkeys tinyint ,@typeuseoldallkeys tinyint ,@typeusenewallkeys tinyint -- -- initialize constants -- select @typeusenew_pk = 0 ,@typeuseold_pk = 1 ,@typeuseoldnonpkkeys = 2 ,@typeusenewnonpkkeys = 3 ,@typeuseoldallkeys = 4 ,@typeusenewallkeys = 5 -- -- validate @proctype -- if (@proctype not in (@typeusenew_pk,@typeuseold_pk,@typeuseoldnonpkkeys, @typeusenewnonpkkeys,@typeuseoldallkeys,@typeusenewallkeys)) begin -- raiserror invalid proctype return 1 end -- -- initialize the vars we will use -- select @pubid = pubid from syspublications where name = @publication select @artid = artid, @dest_table = dest_table, @dest_owner = dest_owner, @ins_cmd = ins_cmd from sysarticles where name = @article and pubid = @pubid select @dest_owner = case when (@dest_owner IS NULL) then N'' else quotename(@dest_owner) + N'.' end ,@fhasnonpkuniquekeys = 0 exec sp_MSget_qualified_name @objid, @qualname OUTPUT -- -- Do we have non PK unique keys -- exec @fhasnonpkuniquekeys = dbo.sp_repltablehasnonpkuniquekey @tabid = @objid -- -- If we are generating refreshing commands -- we will be using cursors -- if (@proctype in (@typeuseoldnonpkkeys,@typeusenewnonpkkeys ,@typeuseoldallkeys,@typeusenewallkeys)) begin -- -- generate cursor to collect PK for selected rowset -- and then perform delete compensation followed by -- insert compensation -- declare @cmd2 nvarchar(4000) ,@cmd3 nvarchar(4000) ,@spacer nvarchar(5) ,@typestring sysname ,@isset int ,@wheremode tinyint ,@suffix nvarchar(10) declare @pkvars table ( c1 int identity NOT NULL, procedure_text nvarchar(4000) collate database_default null) declare @pkcols table ( c1 int identity NOT NULL, procedure_text nvarchar(4000) collate database_default null) -- -- if the article has no non PK unique keys -- we should not be processing for @typeuseoldnonpkkeys,@typeusenewnonpkkeys -- if (@fhasnonpkuniquekeys = 0 and @proctype in (@typeuseoldnonpkkeys,@typeusenewnonpkkeys)) begin -- error we should not generate these cases if there are no non PK unique keys return 1 end -- -- build some strings for PK columns and PK variables (@pkc1..) -- exec dbo.sp_getarticlepkcolbitmap @objid, @pkcolumns output select @cmd = N'' ,@cmd2 = N'' ,@cmd3 = N'' ,@spacer = N'' declare #hccolid cursor local fast_forward for select colid, name from syscolumns where id = @objid order by colid asc open #hccolid fetch #hccolid into @this_col, @colname while (@@fetch_status != -1) begin exec @isset = dbo.sp_isarticlecolbitset @this_col, @pkcolumns if @isset != 0 and (@colname is not null) begin exec dbo.sp_gettypestring @objid, @this_col, @typestring output select @cmd = @cmd + @spacer + N'@pkc' + convert( nvarchar, @this_col ) + N' ' + @typestring ,@cmd2 = @cmd2 + @spacer + quotename(@colname) ,@cmd3 = @cmd3 + @spacer + N'@pkc' + convert( nvarchar, @this_col ) select @spacer = N',' if len( @cmd ) > 3000 begin insert into @pkvars(procedure_text) values( @cmd ) select @cmd = N'' end if len( @cmd2 ) > 3000 begin insert into @pkcols(procedure_text) values( @cmd2 ) select @cmd2 = N'' end if len( @cmd3 ) > 3000 begin insert into @pkfetch(procedure_text) values( @cmd3 ) select @cmd3 = N'' end end fetch #hccolid into @this_col, @colname end close #hccolid deallocate #hccolid if len(@cmd) > 0 insert into @pkvars(procedure_text) values( @cmd ) if len(@cmd2) > 0 insert into @pkcols(procedure_text) values( @cmd2 ) if len(@cmd3) > 0 insert into @pkfetch(procedure_text) values( @cmd3 ) -- -- script the PK variable declare now -- if (@fdodeclare = 1) begin select @cmd = N' declare ' insert into #proctext(procedure_text) values( @cmd ) insert into #proctext(procedure_text) select procedure_text from @pkvars order by c1 asc end -- -- script the cursor declare now -- select @cmd = N' declare #hccompins cursor local fast_forward for select distinct ' insert into #proctext(procedure_text) values( @cmd ) insert into #proctext(procedure_text) select procedure_text from @pkcols order by c1 asc select @cmd = N' from ' + @qualname insert into #proctext(procedure_text) values( @cmd ) -- -- script the where clause for the cursor -- based on @proctype -- if (@proctype = @typeuseoldnonpkkeys) begin select @wheremode = 7 ,@suffix = N'_old' end else if (@proctype = @typeusenewnonpkkeys) begin select @wheremode = 7 ,@suffix = NULL end else if (@proctype = @typeuseoldallkeys) begin select @wheremode = 6 ,@suffix = N'_old' end else if (@proctype = @typeusenewallkeys) begin select @wheremode = 6 ,@suffix = NULL end exec @rc = sp_replscriptuniquekeywhereclause @tabid = @objid ,@columns = @columns ,@prefix = N'@c' ,@suffix = @suffix ,@mode = @wheremode -- -- script the cursor open and fetch -- select @cmd = N' open #hccompins fetch #hccompins into ' insert into #proctext(procedure_text) values( @cmd ) insert into #proctext(procedure_text) select procedure_text from @pkfetch order by c1 asc select @cmd = N' while (@@fetch_status != -1) begin ' insert into #proctext(procedure_text) values( @cmd ) -- -- we are processing a refresh process -- issue a compensating delete for the row -- select in the cursor -- select @cmd = N' ' + '-- ' + '-- Issue a delete command for row selected in cursor ' + '-- ' insert into #proctext(procedure_text) values( @cmd ) select @cmd = N' select @cmd = ''DELETE ' + master.dbo.fn_MSgensqescstr(@dest_owner) collate database_default + quotename(master.dbo.fn_MSgensqescstr(@dest_table) collate database_default) + N' '' + ' insert into #proctext(procedure_text) values( @cmd ) exec @rc = sp_replscriptuniquekeywhereclause @tabid = @objid ,@columns = @columns ,@prefix = N'@pkc' ,@suffix = NULL ,@mode = 8 -- -- script the send for this command -- exec sp_MSscript_compensating_send @pubid, @artid, 0, 1 -- -- continue scripting -- select @cmd = N' ' + '-- ' + '-- Issue a compensating insert for row selected in cursor ' + '-- ' insert into #proctext(procedure_text) values( @cmd ) end -- -- The compensating command will be split into one or more -- fragment commands if the length exceeds 3450 characters in length -- (to accomodate compensating server/db names) -- For correctly estimating the length of the compensating command -- we have to take the max column length of the data into consideration along -- with the scripting command length -- -- -- use the insert command if available -- select @commandlen = 0 ,@setprefix = 1 if (@ins_cmd = N'SQL') begin select @cmd = N' select @cmd = ''INSERT INTO ' + master.dbo.fn_MSgensqescstr(@dest_owner) collate database_default + quotename(master.dbo.fn_MSgensqescstr(@dest_table) collate database_default) + N''' + '' SELECT '' + ' end else begin select @cmd = N' select @cmd = ''EXEC ' + substring(@ins_cmd, 5, len(@ins_cmd) - 4) + N' '' + ' end insert into #proctext(procedure_text) values( @cmd ) select @commandlen = @commandlen + len(@cmd) select @num_col = 0 DECLARE hCColid CURSOR LOCAL FAST_FORWARD FOR select colid, length from syscolumns where id = @objid order by colid asc OPEN hCColid FETCH hCColid INTO @this_col, @collen WHILE (@@fetch_status != -1) begin exec @rc = dbo.sp_MSget_colinfo @objid, @this_col, @columns, 1, @colname output, @ccoltype output if @rc = 0 and EXISTS (select name from syscolumns where id=@objid and colid=@this_col and iscomputed<>1) begin if rtrim(@ccoltype) not like N'timestamp' begin select @num_col = @num_col + 1 -- -- Compute the command fragment length needed for this column -- based on the coltype -- if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('ntext','text','image')) begin -- -- For compensating commands we have to include the text and image data -- as the custom procs used by Distribution process expects them - as it -- done for regular transactional replication - but we will only send NULLs -- as it is not possible to ascertain the size of the data during the generation -- select @cast_str = N' ''null'' ' select @fullcastlen = len(@cast_str) select @fragmentlen = @fullcastlen + 4 + @collen end else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('varchar','nvarchar','char','nchar')) begin if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('nvarchar', 'nchar')) select @collen = (@collen / 2) select @cast_str = case when (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('nvarchar', 'nchar')) then N' ISNULL(''N'''''' + master.dbo.fn_MSgensqescstr(' + quotename(@colname) + N') collate database_default + '''''''', ''null'') ' else N' ISNULL('''''''' + master.dbo.fn_MSgensqescstr(' + quotename(@colname) + N') collate database_default + '''''''', ''null'') ' end select @fullcastlen = len(@cast_str) select @fragmentlen = @fullcastlen + 4 + @collen end else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('binary','varbinary')) begin -- -- each byte has 2 nibbles - we need a char to represent each nibble -- select @collen = @collen * 2 select @cast_str = N' ISNULL(master.dbo.fn_varbintohexsubstring(1,' + quotename(@colname) + N',1,0) collate database_default, ''null'') ' select @fullcastlen = len(@cast_str) select @fragmentlen = @fullcastlen + 4 + @collen + 2 end else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('bit','bigint','int','smallint','tinyint','decimal','numeric')) begin select @collen = 40 select @cast_str = N' ISNULL(CAST(' + quotename(@colname) + N' as nvarchar), ''null'') ' select @fragmentlen = len(@cast_str) + @collen end else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('float','real')) begin select @collen = 60 select @cast_str = N' ISNULL(CONVERT(nvarchar(60),' + quotename(@colname) + N',2), ''null'') ' select @fragmentlen = len(@cast_str) + @collen end else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('money','smallmoney')) begin select @collen = 40 select @cast_str = N' ISNULL(CONVERT(nvarchar(40),' + quotename(@colname) + N',2), ''null'') ' select @fragmentlen = len(@cast_str) + @collen end else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) = 'uniqueidentifier') begin select @collen = 40 select @cast_str = N' ISNULL('''''''' + CAST(' + quotename(@colname) + N' as nvarchar(40)) + '''''''', ''null'') ' select @fragmentlen = len(@cast_str) + @collen end else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('datetime','smalldatetime')) begin select @collen = 40 select @cast_str = N' ISNULL('''''''' + CONVERT(nvarchar(40), ' + quotename(@colname) + N', 112) + N'' '' + CONVERT(nvarchar(40), ' + quotename(@colname) + N', 114) + '''''''', ''null'') ' select @fragmentlen = len(@cast_str) + @collen end else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) = 'sql_variant') begin -- -- need to revisit this later -- select @cast_str = N' ISNULL(master.dbo.fn_sqlvarbasetostr(' + quotename(@colname) + N' ) collate database_default, ''null'') ' select @fragmentlen = len(@cast_str) + @collen end else begin select @collen = 40 select @cast_str = N' ISNULL(CAST(' + quotename(@colname) + N' as nvarchar), ''null'') ' select @fragmentlen = len(@cast_str) + @collen end -- -- for fixed datatypes - we will not split the data at all we will -- flush the command script and continue -- for varying/large datatypes, we will have to split data if necessary -- if ((lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('varchar','nvarchar','char','nchar','binary','varbinary')) and (@fragmentlen + @commandlen > 3450)) begin -- -- the column length is too big, we have to break the data string -- initialize -- if (@num_col = 1) begin select @column_string = N' ' end else begin select @column_string = N' + '','' + ' end -- -- use substring to break the string value in the -- compensating command -- select @first_time = 1 ,@startoffset = 1 while (@collen > 0) begin select @splitlen = case when ((@first_time = 1) or (@collen > 3450)) then (3450 - @commandlen - 30 - @fullcastlen) else @collen end if (@splitlen < 1) begin -- -- we have overcompensated the splitlen -- set to half of the column length -- select @splitlen = @collen / 2 end -- -- Do we need to put quotes (many datatypes need it) -- if (@first_time = 1) begin if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('varchar','nvarchar','char','nchar')) select @column_string = case when (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('nvarchar', 'nchar')) then @column_string + N' ISNULL(''N'''''' + master.dbo.fn_MSgensqescstr( ' else @column_string + N' ISNULL('''''''' + master.dbo.fn_MSgensqescstr( ' end else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('binary','varbinary')) select @column_string = @column_string + N' ISNULL(master.dbo.fn_varbintohexsubstring(1,' end else begin if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('varchar','nvarchar','char','nchar')) select @column_string = N' + ISNULL(master.dbo.fn_MSgensqescstr( ' else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('binary','varbinary')) select @column_string = @column_string + N' + ISNULL(master.dbo.fn_varbintohexsubstring(0,' end -- -- prepare the substring script -- if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('varchar','nvarchar','char','nchar')) select @cast_str = N'SUBSTRING(' + quotename(@colname) + N', ' + cast(@startoffset as nvarchar) + N', ' + cast(@splitlen as nvarchar) + N')' else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('binary','varbinary')) select @cast_str = quotename(@colname) + N', ' + cast(@startoffset as nvarchar) + N', ' + cast((@splitlen/2) as nvarchar) if (@first_time = 1) begin select @cast_str = @cast_str + N') collate database_default, ''null'') ' ,@first_time = 0 end else begin if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('varchar','nvarchar','char','nchar')) begin -- -- for strings the last fragment needs the single -- quote to be added for the string -- select @cast_str = @cast_str + N') collate database_default ' select @cast_str = case when (@collen - @splitlen < 1) then @cast_str + N'+ '''''''', '''') ' else @cast_str + N', '''') ' end end else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('binary','varbinary')) select @cast_str = @cast_str + N') collate database_default, '''') ' end select @column_string = @column_string + @cast_str insert into #proctext(procedure_text) values( @column_string ) if (@fragmentlen + @commandlen > 3450) begin select @cmd = N' from ' + @qualname insert into #proctext(procedure_text) values( @cmd ) -- -- script the where clause -- if (@proctype in (@typeusenew_pk,@typeuseold_pk)) begin -- -- only PK unique key -- if (@proctype = @typeuseold_pk) exec dbo.sp_MSscript_where_clause @objid, @columns, 'upd version', NULL, 0, 'del' else exec dbo.sp_MSscript_where_clause @objid, @columns, 'new_pk_q', NULL, 0, 'ins' end else begin -- -- we are in the cursor for qualifying row -- exec dbo.sp_scriptpkwhereclause @src_objid = @objid ,@pkcolumns = @pkcolumns ,@prefix = N'@pkc' ,@artcolumns = @columns ,@mode = 2 end -- -- Script the compensating send -- exec sp_MSscript_compensating_send @pubid, @artid, 1, @setprefix if (@setprefix = 1) select @setprefix = 0 select @cmd = N' select @cmd = N''''' insert into #proctext(procedure_text) values( @cmd ) select @commandlen = 0 end else select @commandlen = @commandlen + len(@column_string) -- -- update vars for next round -- select @collen = @collen - @splitlen ,@column_string = N'' ,@startoffset = case when (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('binary','varbinary')) then (@splitlen/2) + @startoffset else @splitlen + @startoffset end select @fragmentlen = @fullcastlen + 4 + @collen end -- -- we done with this column now -- skip processing further and continue -- select @commandlen = @commandlen + len(@column_string) end else begin -- -- Handling general fixed type column cases -- if (@num_col = 1) begin select @column_string = N' ' + @cast_str end else begin select @column_string = N' + '','' + ' + @cast_str end -- -- check if we need to flush the command first -- if (@fragmentlen + len(@column_string) + @commandlen > 3450) begin -- -- send this compensating command first -- select @cmd = N' from ' + @qualname insert into #proctext(procedure_text) values( @cmd ) -- -- script the where clause -- if (@proctype in (@typeusenew_pk,@typeuseold_pk)) begin -- -- only PK unique key -- if (@proctype = @typeuseold_pk) exec dbo.sp_MSscript_where_clause @objid, @columns, 'upd version', NULL, 0, 'del' else exec dbo.sp_MSscript_where_clause @objid, @columns, 'new_pk_q', NULL, 0, 'ins' end else begin -- -- we are in the cursor for qualifying row -- exec dbo.sp_scriptpkwhereclause @src_objid = @objid ,@pkcolumns = @pkcolumns ,@prefix = N'@pkc' ,@artcolumns = @columns ,@mode = 2 end -- -- Script the compensating send -- exec sp_MSscript_compensating_send @pubid, @artid, 1, @setprefix if (@setprefix = 1) select @setprefix = 0 select @cmd = N' select @cmd = N'' ''' insert into #proctext(procedure_text) values( @cmd ) select @commandlen = 0 end -- -- script out the column string -- insert into #proctext(procedure_text) values( @column_string ) -- -- if we are processing sql_variants, flush the command again -- if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) = 'sql_variant') begin -- -- send this compensating command first -- select @cmd = N' from ' + @qualname insert into #proctext(procedure_text) values( @cmd ) -- -- script the where clause -- if (@proctype in (@typeusenew_pk,@typeuseold_pk)) begin -- -- only PK unique key -- if (@proctype = @typeuseold_pk) exec dbo.sp_MSscript_where_clause @objid, @columns, 'upd version', NULL, 0, 'del' else exec dbo.sp_MSscript_where_clause @objid, @columns, 'new_pk_q', NULL, 0, 'ins' end else begin -- -- we are in the cursor for qualifying row -- exec dbo.sp_scriptpkwhereclause @src_objid = @objid ,@pkcolumns = @pkcolumns ,@prefix = N'@pkc' ,@artcolumns = @columns ,@mode = 2 end -- -- Script the compensating send -- exec sp_MSscript_compensating_send @pubid, @artid, 1, @setprefix if (@setprefix = 1) select @setprefix = 0 select @cmd = N' select @cmd = N'' ''' insert into #proctext(procedure_text) values( @cmd ) select @commandlen = 0 end else select @commandlen = @commandlen + @fragmentlen + len(@column_string) end end end -- -- process the next column -- FETCH hCColid INTO @this_col, @collen end CLOSE hCColid DEALLOCATE hCColid -- -- Check if we need to flush the command one more time (final) -- if (@commandlen > 0) begin -- -- send the last fragment of the command -- select @cmd = N' from ' + @qualname insert into #proctext(procedure_text) values( @cmd ) -- -- script the where clause -- if (@proctype in (@typeusenew_pk,@typeuseold_pk)) begin -- -- only PK unique key -- if (@proctype = @typeuseold_pk) exec dbo.sp_MSscript_where_clause @objid, @columns, 'upd version', NULL, 0, 'del' else exec dbo.sp_MSscript_where_clause @objid, @columns, 'new_pk_q', NULL, 0, 'ins' end else begin -- -- we are in the cursor for qualifying row -- exec dbo.sp_scriptpkwhereclause @src_objid = @objid ,@pkcolumns = @pkcolumns ,@prefix = N'@pkc' ,@artcolumns = @columns ,@mode = 2 end -- -- Script the compensating send -- exec sp_MSscript_compensating_send @pubid, @artid, 0, @setprefix end -- -- More scripting for refresh command processing modes -- if (@proctype in (@typeuseoldnonpkkeys,@typeusenewnonpkkeys ,@typeuseoldallkeys,@typeusenewallkeys)) begin -- -- script the cursor fetch -- select @cmd = N' fetch #hccompins into ' insert into #proctext(procedure_text) values( @cmd ) insert into #proctext(procedure_text) select procedure_text from @pkfetch order by c1 asc -- -- script the cursor close and deallocate -- select @cmd = N' end close #hccompins deallocate #hccompins ' insert into #proctext(procedure_text) values( @cmd ) end -- -- all done -- return 0 end go exec dbo.sp_MS_marksystemobject sp_MSscript_compensating_insert go -------------------------------------------------------------------------------- --. sp_MSscript_delete_pubwins -------------------------------------------------------------------------------- if exists (select * from sysobjects where type = 'P' and name = 'sp_MSscript_delete_pubwins') drop procedure sp_MSscript_delete_pubwins go raiserror('Creating procedure sp_MSscript_delete_pubwins', 0,1) go create procedure sp_MSscript_delete_pubwins ( @publication sysname -- publication name ,@article sysname -- article name ,@objid int -- object id ,@columns binary(32) -- columns replicated ) as begin declare @cmd nvarchar(4000) ,@artid int ,@pubid int ,@dest_table sysname ,@dest_owner nvarchar(260) ,@rc int ,@qualname nvarchar(512) ,@fhasnonpkuniquekeys int -- -- initialize the vars we will use -- select @pubid = pubid from syspublications where name = @publication select @artid = artid, @dest_table = dest_table, @dest_owner = dest_owner from sysarticles where name = @article and pubid = @pubid select @dest_owner = case when (@dest_owner IS NULL) then N'' else quotename(@dest_owner) + N'.' end exec sp_MSget_qualified_name @objid, @qualname OUTPUT -- -- start scripting -- select @cmd = N' if (@execution_mode = @QPubWins) begin '+N'-- '+N'-- Publisher wins resolution '+N'-- ' insert into #proctext(procedure_text) values( @cmd ) -- -- declare fetch variables for cursor -- exec @rc = sp_scriptpubwinsrefreshcursorvars @objid -- -- continue scripting -- select @cmd = N' '+N'-- '+N'-- -------------------------------------------------------------------- '+N'-- Perform refresh(delete+insert) generations next '+N'-- -------------------------------------------------------------------- '+N'-- '+N'-- '+N'-- Generate delete+insert for values of all keys '+N'-- if (@cftcase in (30,31)) begin' insert into #proctext(procedure_text) values( @cmd ) -- -- generate delete compensating command for OLD values of all unique keys -- select @cmd = N' select @cmd = ''DELETE ' + master.dbo.fn_MSgensqescstr(@dest_owner) collate database_default + quotename(master.dbo.fn_MSgensqescstr(@dest_table) collate database_default) + N' '' + ' insert into #proctext(procedure_text) values( @cmd ) exec @rc = sp_replscriptuniquekeywhereclause @tabid = @objid ,@columns = @columns ,@prefix = N'@c' ,@suffix = N'_old' ,@mode = 4 -- -- script the sending command -- exec sp_MSscript_compensating_send @pubid, @artid, 0, 1 -- -- script the refresh commands for OLD values of all unique keys -- exec @rc = dbo.sp_MSscript_compensating_insert @publication, @article, @objid, @columns, 4, 0 if (@rc != 0 or @@error != 0) return 1 -- -- continue scripting -- select @cmd = N' end '+N'-- '+N'-- -------------------------------------------------------------------- '+N'-- all done for conflict resolution for Publisher Wins policy '+N'-- -------------------------------------------------------------------- '+N'-- end' insert into #proctext(procedure_text) values( @cmd ) -- -- all done -- return 0 end go exec dbo.sp_MS_marksystemobject sp_MSscript_delete_pubwins go -------------------------------------------------------------------------------- --. sp_MSscript_delete_subwins -------------------------------------------------------------------------------- if exists (select * from sysobjects where type = 'P' and name = 'sp_MSscript_delete_subwins') drop procedure sp_MSscript_delete_subwins go raiserror('Creating procedure sp_MSscript_delete_subwins', 0,1) go create procedure sp_MSscript_delete_subwins ( @publication sysname -- publication name ,@article sysname -- article name ,@objid int -- object id ,@columns binary(32) -- columns replicated ) as begin declare @cmd nvarchar(4000) ,@qualname nvarchar(512) exec sp_MSget_qualified_name @objid, @qualname OUTPUT -- -- start scripting -- select @cmd = N' if (@execution_mode = @QSubWins) begin '+N'-- '+N'-- Subscriber wins resolution '+N'-- if (@cftcase = 31) begin '+N'-- '+N'-- delete row with PK '+N'-- ' insert into #proctext(procedure_text) values( @cmd ) -- -- generate the delete statement -- select @cmd = N' delete ' + @qualname insert into #proctext(procedure_text) values( @cmd ) exec dbo.sp_MSscript_where_clause @objid, @columns, 'upd version', NULL, 0 -- -- continue with scripting -- select @cmd = N' end '+N'-- '+N'-- -------------------------------------------------------------------- '+N'-- all done for conflict resolution for Subscriber Wins policy '+N'-- -------------------------------------------------------------------- '+N'-- end' insert into #proctext(procedure_text) values( @cmd ) -- -- all done -- return 0 end go exec dbo.sp_MS_marksystemobject sp_MSscript_delete_subwins go -------------------------------------------------------------------------------- --. sp_MSscript_sync_ins_proc -------------------------------------------------------------------------------- if exists (select * from sysobjects where type = 'P' and name = 'sp_MSscript_sync_ins_proc') drop procedure sp_MSscript_sync_ins_proc go raiserror('Creating procedure sp_MSscript_sync_ins_proc', 0,1) go create procedure sp_MSscript_sync_ins_proc ( @publication sysname, @article sysname, @procname sysname) as BEGIN declare @source_objid int ,@colname sysname ,@indid int ,@cmd nvarchar(4000) ,@columns binary(32) ,@outvars nvarchar(4000) ,@rc int ,@error_cmd tinyint ,@identity_insert bit ,@queued_pub bit set nocount on -- -- security check -- exec @rc = dbo.sp_MSreplcheck_publish if @@error <> 0 or @rc <> 0 begin return (1) end -- -- Create temp table -- create table #proctext ( c1 int identity NOT NULL, procedure_text nvarchar(4000) collate database_default null) select @queued_pub = allow_queued_tran from syspublications where name = @publication -- -- proc definition -- exec @rc = dbo.sp_MSscript_beginproc @publication, @article, @procname, @source_objid output, @columns output if @rc = 0 return -- -- construct parameter list -- exec dbo.sp_MSscript_params @source_objid, @columns, null, 1, @outvars output -- -- add other parameters and start body of proc -- exec dbo.sp_MSscript_procbodystart @queued_pub -- -- script out security check -- exec dbo.sp_MSscript_security @publication -- -- script the execution mode checks -- exec dbo.sp_MSscript_ExecutionMode_stmt @publication, @article, 0 -- -- script out subscription validation -- exec dbo.sp_MSscript_validate_subscription @publication, @article -- -- Work around for case where article has 1 col that is not user-modfied (identity, timestamp) -- *** Do we need to check this here - -- *** we should be checking this when creating subscription -- exec @rc = dbo.sp_MStable_not_modifiable @source_objid, @columns if @rc = 1 select @error_cmd = 1 else begin exec @indid = dbo.sp_MStable_has_unique_index @source_objid if (@outvars != null and @indid = 0) -- no insert/update allowed if timestamp/identity col and no unique index select @error_cmd = 1 else select @error_cmd = 0 end if (@error_cmd = 0) begin -- Continue generation -- Check to see if identity insert must be turned on -- i.e. Does the table has identity that are included in the partition? exec sp_MSis_identity_insert @publication, @article, @identity_insert output -- -- script insert statemnt -- exec dbo.sp_MSscript_insert_statement @source_objid, @columns, @identity_insert, @queued_pub -- -- script queued specific stuff -- if (@queued_pub = 1) begin -- -- script Conflict finder -- exec dbo.sp_MSscriptinsertconflictfinder @publication, @article, @source_objid, @columns -- -- script Conflict resolution block for Publisher Wins case -- exec dbo.sp_MSscript_insert_pubwins @publication, @article, @source_objid, @columns -- -- script Conflict resolution block for Subscriber Wins case -- exec dbo.sp_MSscript_insert_subwins @publication, @article, @source_objid, @columns, @identity_insert end -- -- script closing -- exec dbo.sp_MSscript_endproc @source_objid, 'ins', @columns, @outvars, @queued_pub end else begin -- -- Generate error command and finish -- insert into #proctext(procedure_text) values( N' exec sp_MSreplraiserror 20516 END ') end -- -- send fragments to client -- select procedure_text from #proctext order by c1 asc END go EXEC dbo.sp_MS_marksystemobject sp_MSscript_sync_ins_proc GO grant execute on dbo.sp_MSscript_sync_ins_proc to public go -------------------------------------------------------------------------------- --. sp_MSscript_sync_upd_proc -------------------------------------------------------------------------------- if exists (select * from sysobjects where type = 'P' and name = 'sp_MSscript_sync_upd_proc') drop procedure sp_MSscript_sync_upd_proc go raiserror('Creating procedure sp_MSscript_sync_upd_proc', 0,1) go create procedure sp_MSscript_sync_upd_proc ( @publication sysname, @article sysname, @procname sysname) as BEGIN declare @source_objid int ,@colname sysname ,@indid int ,@cmd nvarchar(4000) ,@columns binary(32) ,@outvars nvarchar(4000) ,@rc int ,@error_cmd tinyint ,@identity_insert bit ,@queued_pub bit set nocount on -- -- security check -- exec @rc = dbo.sp_MSreplcheck_publish if @@error <> 0 or @rc <> 0 begin return (1) end -- -- Create temp table -- create table #proctext ( c1 int identity NOT NULL, procedure_text nvarchar(4000) collate database_default null) select @queued_pub = allow_queued_tran from syspublications where name = @publication -- -- proc definition -- exec @rc = dbo.sp_MSscript_beginproc @publication, @article, @procname, @source_objid output, @columns output if @rc = 0 return -- -- construct parameter list -- Script bitmap parameter -- exec dbo.sp_MSscript_params @source_objid, @columns, null, 1, @outvars output insert into #proctext(procedure_text) values( N',') exec dbo.sp_MSscript_params @source_objid, @columns, N'_old', 0, null insert into #proctext(procedure_text) values( N' ,@bitmap varbinary(4000)') -- -- add other parameters and start body of proc -- exec dbo.sp_MSscript_procbodystart @queued_pub -- -- script out security check -- exec dbo.sp_MSscript_security @publication -- -- script the execution mode checks -- exec dbo.sp_MSscript_ExecutionMode_stmt @publication, @article, 2 -- -- script out subscription validation -- exec dbo.sp_MSscript_validate_subscription @publication, @article -- -- Work around for case where article has 1 col that is not user-modfied (identity, timestamp) -- *** Do we need to check this here - -- *** we should be checking this when creating subscription -- exec @rc = dbo.sp_MStable_not_modifiable @source_objid, @columns if @rc = 1 select @error_cmd = 1 else begin exec @indid = dbo.sp_MStable_has_unique_index @source_objid if (@outvars != null and @indid = 0) -- no insert/update allowed if timestamp/identity col and no unique index select @error_cmd = 1 else select @error_cmd = 0 end if (@error_cmd = 0) begin -- Continue generation -- -- script update statemnt -- exec dbo.sp_MSscript_update_statement @publication, @article, @source_objid, @columns, @queued_pub -- -- script queued specific stuff -- if (@queued_pub = 1) begin -- Check to see if identity insert must be turned on -- i.e. Does the table has identity that are included in the partition? exec sp_MSis_identity_insert @publication, @article, @identity_insert output -- -- script the conflict resolution logic common to all resolution policies -- exec dbo.sp_MSscriptupdateconflictfinder @publication, @article, @source_objid, @columns -- -- script Conflict resolution block for Publisher Wins case -- exec dbo.sp_MSscript_update_pubwins @publication, @article, @source_objid, @columns -- -- script Conflict resolution block for Subscriber Wins case -- exec dbo.sp_MSscript_update_subwins @publication, @article, @source_objid, @columns, @identity_insert end -- -- script closing -- exec dbo.sp_MSscript_endproc @source_objid, 'upd', @columns, @outvars, @queued_pub end else begin -- -- Generate error command and finish -- insert into #proctext(procedure_text) values( N' exec sp_MSreplraiserror 20516 END ') end -- -- send fragments to client -- select procedure_text from #proctext order by c1 asc END go exec dbo.sp_MS_marksystemobject sp_MSscript_sync_upd_proc GO grant execute on dbo.sp_MSscript_sync_upd_proc to public go -------------------------------------------------------------------------------- --. sp_MSscript_sync_del_proc -------------------------------------------------------------------------------- if exists (select * from sysobjects where type = 'P' and name = 'sp_MSscript_sync_del_proc') drop procedure sp_MSscript_sync_del_proc go raiserror('Creating procedure sp_MSscript_sync_del_proc', 0,1) go create procedure sp_MSscript_sync_del_proc ( @publication sysname, @article sysname, @procname sysname) as BEGIN declare @source_objid int declare @colname sysname declare @indid int declare @cmd nvarchar(4000) declare @columns binary(32) declare @outvars nvarchar(4000) declare @rc int declare @error_cmd tinyint declare @queued_pub bit set nocount on -- -- security check -- exec @rc = dbo.sp_MSreplcheck_publish if @@error <> 0 or @rc <> 0 begin return (1) end -- -- Create temp table -- create table #proctext ( c1 int identity NOT NULL, procedure_text nvarchar(4000) collate database_default null) select @queued_pub = allow_queued_tran from syspublications where name = @publication -- -- proc definition -- exec @rc = dbo.sp_MSscript_beginproc @publication, @article, @procname, @source_objid output, @columns output if @rc = 0 return -- -- construct parameter list -- exec dbo.sp_MSscript_params @source_objid, @columns, N'_old', 0, null -- -- add other parameters and start body of proc -- exec dbo.sp_MSscript_procbodystart @queued_pub -- -- script out security check -- exec dbo.sp_MSscript_security @publication -- -- script the execution mode checks -- exec dbo.sp_MSscript_ExecutionMode_stmt @publication, @article, 1 -- -- script out subscription validation -- exec dbo.sp_MSscript_validate_subscription @publication, @article -- -- Work around for case where article has 1 col that is not user-modfied (identity, timestamp) -- *** Do we need to check this here - -- *** we should be checking this when creating subscription -- exec @rc = dbo.sp_MStable_not_modifiable @source_objid, @columns if @rc = 1 select @error_cmd = 1 else begin exec @indid = dbo.sp_MStable_has_unique_index @source_objid if (@outvars != null and @indid = 0) -- no insert/update allowed if timestamp/identity col and no unique index select @error_cmd = 1 else select @error_cmd = 0 end if (@error_cmd = 0) begin -- Continue generation -- -- script delete statemnt -- exec dbo.sp_MSscript_delete_statement @publication, @article, @source_objid, @columns, @queued_pub -- -- script queued specific stuff -- if (@queued_pub = 1) begin -- -- script the conflict resolution logic common to all resolution policies -- exec sp_MSscriptdelconflictfinder @publication, @article, @source_objid, @columns -- -- script Conflict resolution block for Publisher Wins case -- exec dbo.sp_MSscript_delete_pubwins @publication, @article, @source_objid, @columns -- -- script Conflict resolution block for Subscriber Wins case -- exec dbo.sp_MSscript_delete_subwins @publication, @article, @source_objid, @columns end -- -- script closing -- exec dbo.sp_MSscript_endproc @source_objid, 'del', @columns, @outvars, @queued_pub end else begin -- -- Generate error command and finish -- insert into #proctext(procedure_text) values( N' exec sp_MSreplraiserror 20516 END ') end -- -- send fragments to client -- select procedure_text from #proctext order by c1 asc END go EXEC dbo.sp_MS_marksystemobject sp_MSscript_sync_del_proc GO grant execute on dbo.sp_MSscript_sync_del_proc to public go -------------------------------------------------------------------------------- --. sp_MSscript_endproc -------------------------------------------------------------------------------- if exists (select * from sysobjects where type = 'P' and name = 'sp_MSscript_endproc') drop procedure sp_MSscript_endproc go raiserror('Creating procedure sp_MSscript_endproc', 0,1) go create procedure sp_MSscript_endproc ( @objid int, @op_type varchar(3) = 'ins', -- 'ins', 'upd', 'del' @columns binary(32), @outvars nvarchar(4000), @queued_pub bit = 0 ) as BEGIN declare @cmd nvarchar(4000) declare @qualname nvarchar(512) exec sp_MSget_qualified_name @objid, @qualname OUTPUT -- -- start scripting -- select @cmd = N' ' + N'-- ' + N'-- decide the return code ' + N'-- if (@execution_mode = @immediate) begin if @error != 0 return -1 -- Return special code to indicate the subscriber row needs to be -- refreshed. if @rowcount = 0 return 5 end' insert into #proctext(procedure_text) values(@cmd) -- -- operation specific stuff -- if (@queued_pub = 1) begin if (@op_type = 'ins') begin select @cmd = N' if (@execution_mode = @QFirstPass) begin if (@rowcount = 0) begin if (@error in (547, 2601, 2627)) return 2 -- insert conflict else return -1 -- error end end' end else if (@op_type = 'upd') begin select @cmd = N' if (@execution_mode = @QFirstPass) begin if (@rowcount = 0) begin if (@error in (0, 547, 2601, 2627)) return 1 -- update conflict else return -1 -- error end end' end else if (@op_type = 'del') begin select @cmd = N' if (@execution_mode = @QFirstPass) begin if (@rowcount = 0) begin if (@error in (0, 547)) return 3 -- delete conflict else return -1 -- error end end' end insert into #proctext(procedure_text) values(@cmd) -- -- continue with scripting -- select @cmd = N' if (@execution_mode in (@QPubWins, @QSubWins)) begin if (@@error != 0 or @retcode != 0) return -1 -- error end ' insert into #proctext(procedure_text) values(@cmd) end -- -- if we have output vars to assign do it now -- if (@outvars is not null) begin if @op_type = 'upd' begin -- -- Script out pk var assigment that used in sp_MSscript_where_clause -- exec dbo.sp_MSscript_pkvar_assignment @objid, @columns, 1, null, null, null, @queued_pub insert into #proctext(procedure_text) values(N' ') end select @cmd = N' select ' + @outvars + N' from ' + @qualname insert into #proctext(procedure_text) values( @cmd) insert into #proctext(procedure_text) values( N' ') if (@op_type = 'ins') exec dbo.sp_MSscript_where_clause @objid, @columns, 'new pk', null, 4 else if (@op_type = 'upd') exec dbo.sp_MSscript_where_clause @objid, @columns, 'old pk', null, 4 end -- -- Final part of the proc -- select @cmd = N' ' + N'-- ' + N'-- past all checks ' + N'-- return 0 END ' insert into #proctext(procedure_text) values(@cmd) -- -- all done -- return 0 END go exec dbo.sp_MS_marksystemobject sp_MSscript_endproc go grant execute on dbo.sp_MSscript_endproc to public go -------------------------------------------------------------------------------- --. sp_scriptinsproc -------------------------------------------------------------------------------- if exists (select * from sysobjects where type = 'P' and name = 'sp_scriptinsproc') drop procedure sp_scriptinsproc go raiserror('Creating procedure sp_scriptinsproc', 0,1) go create procedure sp_scriptinsproc ( @artid int) as BEGIN declare @cmd nvarchar(4000) ,@dest_owner nvarchar(255) ,@dest_tabname sysname ,@src_objid int ,@columns binary(32) ,@ins_cmd nvarchar(255) ,@dest_proc sysname ,@this_col int ,@art_col int ,@isset int ,@pubid int ,@identity_insert bit ,@rc int ,@colname sysname ,@ccoltype sysname ,@typestring nvarchar(255) ,@spacer nvarchar(1) ,@queued_check bit ,@column_string nvarchar(4000) ,@var_string nvarchar(4000) set nocount on -- -- security check -- exec @rc = dbo.sp_MSreplcheck_publish if @@error <> 0 or @rc <> 0 begin return (1) end if not exists( select * from sysarticles where artid = @artid AND (type & 1) = 1 ) begin raiserror (14155, 16, 1 ) return 1 end -------- create temp table for command fragments and insert column list create table #proctext ( c1 int identity NOT NULL, procedure_text nvarchar(4000) collate database_default null) create table #collisttab ( c1 int identity NOT NULL, procedure_text nvarchar(4000) collate database_default null) -------- get sysarticles information select @dest_owner = dest_owner, @dest_tabname = dest_table, @src_objid = objid, @columns = columns, @ins_cmd = ins_cmd, @pubid = pubid from sysarticles where artid = @artid -- Check to see if identity insert must be turned on -- i.e. Does the table has identity that are included in the partition? exec sp_MSis_identity_insert null, null, @identity_insert output, @artid -- if @dest_owner is not null begin select @dest_owner = QUOTENAME( @dest_owner ) + N'.' end else begin select @dest_owner = N'' end -- Check if this is a queued publication select @queued_check = ISNULL(allow_queued_tran, 0) from syspublications where pubid = @pubid -------- get dest proc name if( 1 != charindex( N'CALL', upper(@ins_cmd collate SQL_Latin1_General_CP1_CS_AS) ) ) or @ins_cmd is null begin raiserror (14156, 16, 1 ) return 1 end select @dest_proc = substring( @ins_cmd, 6, len( @ins_cmd ) - 4 ) select @cmd = N'if exists (select * from sysobjects where type = ''P'' and name = ''' + replace(@dest_proc, N'''', N'''''') + N''') drop proc ' + QUOTENAME(@dest_proc) insert into #proctext(procedure_text) values( @cmd ) insert into #proctext(procedure_text) values( N'go' ) select @cmd = N'create procedure ' + QUOTENAME(@dest_proc) -------- construct parameter list select @art_col = 1 select @spacer = N' ' DECLARE hCColid CURSOR LOCAL FAST_FORWARD FOR select colid from syscolumns where id = @src_objid order by colid asc OPEN hCColid FETCH hCColid INTO @this_col WHILE (@@fetch_status <> -1) begin exec @isset = dbo.sp_isarticlecolbitset @this_col, @columns if @isset != 0 and EXISTS (select name from syscolumns where id=@src_objid and @this_col=colid and iscomputed<>1) begin if len( @cmd ) > 3000 begin insert into #proctext(procedure_text) values( @cmd ) select @cmd = N'' end exec dbo.sp_gettypestring @src_objid, @this_col, @typestring OUTPUT select @cmd = @cmd + @spacer + N'@c' + convert( nvarchar, @art_col ) + N' ' + @typestring select @art_col = @art_col + 1 select @spacer = N',' end FETCH hCColid INTO @this_col end CLOSE hCColid DEALLOCATE hCColid -- save off cmd fragment insert into #proctext(procedure_text) values( @cmd ) select @cmd = N' AS BEGIN ' insert into #proctext(procedure_text) values( @cmd ) ------- construct proc body -- Generate strings for col names and variables select @art_col = 0 select @spacer = N' ' DECLARE hCColid CURSOR LOCAL FAST_FORWARD FOR select colid from syscolumns where id = @src_objid order by colid asc OPEN hCColid FETCH hCColid INTO @this_col WHILE (@@fetch_status <> -1) begin exec @rc = dbo.sp_MSget_colinfo @src_objid, @this_col, @columns, 1, @colname output, @ccoltype output if @rc = 0 and EXISTS (select name from syscolumns where id=@src_objid and colid=@this_col and iscomputed<>1) begin select @art_col = @art_col + 1 if (@art_col = 1) begin select @column_string = QUOTENAME(@colname) select @var_string = N'@c' + cast(@art_col as nvarchar(4)) end else begin select @column_string = @column_string + N', ' + QUOTENAME(@colname) select @var_string = @var_string + N', @c' + cast(@art_col as nvarchar(4)) end -- transfer column list string to table if too large if (len(@column_string) > 3000) begin insert into #collisttab(procedure_text) values( @column_string ) select @column_string = ' ' end end FETCH hCColid INTO @this_col end CLOSE hCColid DEALLOCATE hCColid -- insert the remaining strings for column list and where clause insert into #collisttab(procedure_text) values( @column_string ) -- -- If we are a part of queued publication then -- insert only if PK or other unique key(s) do not exist -- if (@queued_check = 1) begin select @cmd = N' if not exists (select * from ' + @dest_owner + QUOTENAME(@dest_tabname) + N' ' insert into #proctext(procedure_text) values( @cmd ) exec @rc = sp_replscriptuniquekeywhereclause @tabid = @src_objid ,@columns = @columns ,@prefix = '@c' ,@mode = 1 if (@@error != 0 or @rc != 0) return 1 select @cmd = N') BEGIN' insert into #proctext(procedure_text) values( @cmd ) end -- set identity_insert on if @identity_insert = 1 begin select @cmd = N' set identity_insert ' + @dest_owner + QUOTENAME(@dest_tabname) + ' on' insert into #proctext(procedure_text) values( @cmd ) end -- -- prepare the insert statement now -- select @cmd = N' insert into ' + @dest_owner + QUOTENAME(@dest_tabname) + N'( ' insert into #proctext(procedure_text) values( @cmd ) insert into #proctext(procedure_text) select procedure_text from #collisttab order by c1 asc select @cmd = N' )' insert into #proctext(procedure_text) values( @cmd ) if @art_col > 0 begin select @cmd = N' values ( ' insert into #proctext(procedure_text) values( @cmd ) insert into #proctext(procedure_text) values( @var_string ) select @cmd = N' ) ' insert into #proctext(procedure_text) values( @cmd ) end -- set identity_insert off if @identity_insert = 1 begin select @cmd = N' set identity_insert ' + @dest_owner + QUOTENAME(@dest_tabname) + ' off' insert into #proctext(procedure_text) values( @cmd ) end -- -- If we are a part of queued publication then -- add the block delimiter -- drop table #collisttab if (@queued_check = 1) begin select @cmd = N' END END' end else select @cmd = N' END' insert into #proctext(procedure_text) values( @cmd ) -- send fragements to client select procedure_text from #proctext order by c1 asc END go EXEC dbo.sp_MS_marksystemobject sp_scriptinsproc GO grant exec on dbo.sp_scriptinsproc to public go -------------------------------------------------------------------------------- --. sp_scriptxupdproc -------------------------------------------------------------------------------- if exists (select * from sysobjects where type = 'P' and name = 'sp_scriptxupdproc') drop procedure sp_scriptxupdproc go raiserror('Creating procedure sp_scriptxupdproc', 0,1) go create procedure sp_scriptxupdproc @artid int as BEGIN declare @cmd nvarchar(4000), @dest_owner nvarchar(255), @dest_tabname sysname, @src_objid int, @artcolumns binary(32), @pkcolumns binary(32), @upd_cmd nvarchar(255), @dest_proc sysname, @this_col int, @art_col int, --@pkart_col int, @isset int, @rc int, @fhasnonpkuniquekeys int, @pkcomputed int, @typestring nvarchar(255), @spacer nvarchar(10), @pubid int, @param_count int ,@queued_check bit, @exists_else bit ,@qwhere_string nvarchar(4000) set nocount on -- -- security check -- exec @rc = dbo.sp_MSreplcheck_publish if @@error <> 0 or @rc <> 0 begin return (1) end if not exists( select * from sysarticles where artid = @artid AND (type & 1) = 1 ) begin raiserror (14155, 16, 1 ) return 1 end select @exists_else = 0 ,@fhasnonpkuniquekeys = 0 -------- create temp table for command fragments create table #proctext ( c1 int identity NOT NULL, procedure_text nvarchar(4000) collate database_default null) -------- get sysarticles information select @pubid = pubid, @dest_owner = dest_owner, @dest_tabname = dest_table, @src_objid = objid, @artcolumns = columns, @upd_cmd = upd_cmd from sysarticles where artid = @artid if @dest_owner is not null begin select @dest_owner = QUOTENAME( @dest_owner ) + N'.' end else begin select @dest_owner = N'' end -- Check if this is a queued publication select @queued_check = ISNULL(allow_queued_tran, 0) from syspublications where pubid = @pubid -------- get dest proc name if( 1 != charindex( N'XCALL', upper(@upd_cmd collate SQL_Latin1_General_CP1_CS_AS) ) ) or @upd_cmd is null begin raiserror (14156, 16, 1 ) return 1 end declare @keep_identity bit if exists (select * from syspublications where pubid = @pubid and allow_queued_tran = 1) and OBJECTPROPERTY(@src_objid, 'tablehasidentity') = 1 select @keep_identity = 1 else select @keep_identity = 0 select @dest_proc = substring( @upd_cmd, 7, len( @upd_cmd ) - 5 ) select @cmd = N'if exists (select * from sysobjects where type = ''P'' and name = ''' + replace(@dest_proc, N'''', N'''''') + N''') drop proc ' + QUOTENAME(@dest_proc) insert into #proctext(procedure_text) values( @cmd ) insert into #proctext(procedure_text) values( N'go' ) insert into #proctext( procedure_text ) values ( N'create procedure ' + QUOTENAME(@dest_proc) + N' ') -------- construct parameter list exec dbo.sp_getarticlepkcolbitmap @src_objid, @pkcolumns output -- Send null as @pkcolumns. We don't need pk parameters. exec dbo.sp_scriptupdateparams @src_objid, @artcolumns, NULL, @param_count output insert into #proctext(procedure_text) values ( N'as' ) -- -- If we are a part of queued publication -- if (@queued_check = 1) begin -- -- Check if we have non PK unique keys -- exec @fhasnonpkuniquekeys = dbo.sp_repltablehasnonpkuniquekey @tabid = @src_objid if (@fhasnonpkuniquekeys = 1) begin -- -- There are non PK unique keys -- update only if updated values of non PK unique key(s) do not exist -- select @cmd = N' if not exists (select * from ' + @dest_owner + QUOTENAME(@dest_tabname) + N' ' insert into #proctext(procedure_text) values( @cmd ) exec @rc = sp_replscriptuniquekeywhereclause @tabid = @src_objid ,@columns = @artcolumns ,@prefix = '@c' ,@mode = 2 ,@paramcount = @param_count if (@@error != 0 or @rc != 0) return 1 select @cmd = N') begin' insert into #proctext(procedure_text) values( @cmd ) end end -------- now create the update statement -- construct test to see if pk has changed -- only do this if the article has columns not included in the pk exec @pkcomputed = sp_MSareallcolumnscomputed @src_objid, @pkcolumns declare @pk_is_identity bit select @pk_is_identity = 0 if @artcolumns != @pkcolumns and @pkcomputed = 0 begin select @cmd = N'if' select @art_col = 1 --select @pkart_col = 1 select @spacer = ' ' DECLARE hCColid CURSOR LOCAL FAST_FORWARD FOR select colid from syscolumns where id = @src_objid order by colid asc OPEN hCColid FETCH hCColid INTO @this_col select @pk_is_identity = 1 WHILE (@@fetch_status <> -1) begin exec @isset = dbo.sp_isarticlecolbitset @this_col, @artcolumns if @isset != 0 and EXISTS (select name from syscolumns where id=@src_objid and @this_col=colid and iscomputed<>1) begin exec @isset = dbo.sp_isarticlecolbitset @this_col, @pkcolumns if @isset != 0 begin if not (@keep_identity = 1 and columnproperty(@src_objid, col_name( @src_objid, @this_col), 'IsIdentity') = 1) begin select @pk_is_identity = 0 select @cmd = @cmd + @spacer + N'@c'+convert( nvarchar, @art_col + @param_count/2) + N' = @c' + convert( nvarchar, @art_col ) select @spacer = N' and ' --select @pkart_col = @pkart_col + 1 if len( @cmd ) > 3000 begin insert into #proctext(procedure_text) values( @cmd ) select @cmd = N'' end end end select @art_col = @art_col + 1 end FETCH hCColid INTO @this_col end CLOSE hCColid DEALLOCATE hCColid if @pk_is_identity = 0 begin insert into #proctext(procedure_text) values( @cmd ) insert into #proctext(procedure_text) values( N'begin' ) -- construct update if pk hasn't changed -- We know that there would be a least one column for the update below, even if -- the columns outside the pk are identity or timestamp. Since identity and timestamp -- will be mapped off unless it is queued tran. In that case, we have 'msrepl_tran_version' select @cmd = N'update ' + @dest_owner + QUOTENAME(@dest_tabname) + N' set' -- create SET clause select @art_col = 1 select @spacer = N' ' DECLARE hCColid CURSOR LOCAL FAST_FORWARD FOR select colid from syscolumns where id = @src_objid order by colid asc OPEN hCColid FETCH hCColid INTO @this_col WHILE (@@fetch_status <> -1) begin exec @isset = dbo.sp_isarticlecolbitset @this_col, @artcolumns if @isset != 0 and EXISTS (select name from syscolumns where id=@src_objid and @this_col=colid and iscomputed<>1) begin exec @isset = dbo.sp_isarticlecolbitset @this_col, @pkcolumns if @isset = 0 begin if not (@keep_identity = 1 and columnproperty(@src_objid, col_name( @src_objid, @this_col), 'IsIdentity') = 1) begin select @cmd = @cmd + @spacer + QUOTENAME(col_name( @src_objid, @this_col)) + N' = @c' + convert( nvarchar, @art_col + @param_count/2) select @spacer = N',' if len( @cmd ) > 3000 begin insert into #proctext(procedure_text) values( @cmd ) select @cmd = N'' end -- -- Queued processing:if this is the row version column : need to add to where clause -- if ((@queued_check = 1) and (col_name( @src_objid, @this_col) = N'msrepl_tran_version')) select @qwhere_string = N' and msrepl_tran_version = @c' + convert( nvarchar, @art_col ) end end select @art_col = @art_col + 1 end FETCH hCColid INTO @this_col end CLOSE hCColid DEALLOCATE hCColid insert into #proctext(procedure_text) values( @cmd ) exec dbo.sp_scriptpkwhereclause @src_objid, @pkcolumns, N'@c', @artcolumns if (@queued_check = 1) insert into #proctext(procedure_text) values( @qwhere_string ) else exec dbo.sp_MSscript_missing_row_check insert into #proctext(procedure_text) values( N'end' ) insert into #proctext(procedure_text) values( N'else' ) insert into #proctext(procedure_text) values( N'begin' ) select @exists_else = 1 end end -- end if artcols != pkcols -- -- If we are a part of queued publication then -- update only if updated value of PK does not exist -- if (@queued_check = 1 and @pk_is_identity = 0) begin select @cmd = N' if not exists (select * from ' + @dest_owner + QUOTENAME(@dest_tabname) + N' ' insert into #proctext(procedure_text) values( @cmd ) exec @rc = sp_replscriptuniquekeywhereclause @tabid = @src_objid ,@columns = @artcolumns ,@prefix = '@c' ,@mode = 3 ,@paramcount = @param_count if (@@error != 0 or @rc != 0) return 1 select @cmd = N') begin' insert into #proctext(procedure_text) values( @cmd ) end -- construct update if pk has changed select @cmd = N'update ' + @dest_owner + QUOTENAME(@dest_tabname) + N' set' -- create SET clause select @art_col = 1 select @spacer = N' ' DECLARE hCColid CURSOR LOCAL FAST_FORWARD FOR select colid from syscolumns where id = @src_objid order by colid asc OPEN hCColid FETCH hCColid INTO @this_col WHILE (@@fetch_status <> -1) begin exec @isset = dbo.sp_isarticlecolbitset @this_col, @artcolumns if @isset != 0 and EXISTS (select name from syscolumns where id=@src_objid and @this_col=colid and iscomputed<>1) begin if not (@keep_identity = 1 and columnproperty(@src_objid, col_name( @src_objid, @this_col), 'IsIdentity') = 1) begin select @cmd = @cmd + @spacer + QUOTENAME(col_name( @src_objid, @this_col)) + N' = @c' + convert( nvarchar, @art_col + @param_count/2 ) select @spacer = N',' if len( @cmd ) > 3000 begin insert into #proctext(procedure_text) values( @cmd ) select @cmd = N'' end -- -- Queued processing:if this is the row version column : need to add to where clause -- if ((@queued_check = 1) and (col_name( @src_objid, @this_col) = N'msrepl_tran_version')) select @qwhere_string = N' and msrepl_tran_version = @c' + convert( nvarchar, @art_col ) end select @art_col = @art_col + 1 end FETCH hCColid INTO @this_col end CLOSE hCColid DEALLOCATE hCColid insert into #proctext(procedure_text) values( @cmd ) exec dbo.sp_scriptpkwhereclause @src_objid, @pkcolumns, N'@c', @artcolumns if (@queued_check = 1) begin insert into #proctext(procedure_text) values( @qwhere_string ) if (@pk_is_identity = 0) begin select @cmd = N' end' insert into #proctext(procedure_text) values( @cmd ) end end else exec dbo.sp_MSscript_missing_row_check if @exists_else = 1 insert into #proctext(procedure_text) values( N'end' ) -- -- End the if exists block for Queued publications -- if (@queued_check = 1) and (@fhasnonpkuniquekeys = 1) begin select @cmd = N' end' insert into #proctext(procedure_text) values( @cmd ) end -- flush to client select procedure_text from #proctext order by c1 asc END go EXEC dbo.sp_MS_marksystemobject sp_scriptxupdproc GO grant exec on dbo.sp_scriptxupdproc to public go -------------------------------------------------------------------------------- --. sp_repltablehasnonpkuniquekey -------------------------------------------------------------------------------- if exists (select * from sysobjects where type = 'P' and name = 'sp_repltablehasnonpkuniquekey') drop procedure sp_repltablehasnonpkuniquekey go raiserror('Creating procedure sp_repltablehasnonpkuniquekey',0,-1) go create procedure sp_repltablehasnonpkuniquekey ( @tabid int -- id of the table ) as begin set nocount on declare @retcode int -- -- security check - should be dbo or sysadmin -- exec @retcode = dbo.sp_MSreplcheck_publish if @@ERROR != 0 or @retcode != 0 return -1 -- -- process if the object is a table and has index -- if (ObjectProperty(@tabid, 'IsTable') = 1) and (ObjectProperty(@tabid, 'TableHasIndex') = 1) begin -- -- Set return flag if it is Unique key but not PK -- if exists (select indid from sysindexes where id = @tabid and indid > 0 and indid < 255 and (status & 2) != 0 and (status & 2048) = 0 ) begin select @retcode = 1 end end -- -- all done -- return @retcode end go exec sp_MS_marksystemobject sp_repltablehasnonpkuniquekey go -------------------------------------------------------------------------------- --. sp_replscriptuniquekeywhereclause -------------------------------------------------------------------------------- if exists (select * from sysobjects where type = 'P' and name = 'sp_replscriptuniquekeywhereclause') drop procedure sp_replscriptuniquekeywhereclause go raiserror('Creating procedure sp_replscriptuniquekeywhereclause', 0,1) go create procedure sp_replscriptuniquekeywhereclause ( @tabid int -- id of the table ,@columns binary(32) -- column map for the article ,@prefix nvarchar(10) = '@c' -- prefix for the scripted column variables ,@suffix nvarchar(10) = null -- suffix for the scripted column variables ,@mode tinyint -- 1 = insert custom proc, -- 2 = upd custom proc non PK, -- 3 = upd custom proc PK only -- 4 = compensating where all keys, -- 5 = compensating where non PK keys, -- 6 = refresh cursor all keys, -- 7 = refresh cursor non PK keys -- 8 = compensating where PK only ,@paramcount int = null -- Total number of parameters - needed for mode = 2 and 3 ) as begin set nocount on declare @retcode int ,@indid int ,@indstatus int ,@indkey int ,@qualname nvarchar(512) ,@colname sysname ,@var sysname ,@artcol int ,@thiscol int ,@cmd nvarchar(4000) ,@findexstarted bit ,@fisfirstindex bit -- -- constants -- ,@modeinscustproc tinyint ,@modeupdcustprocnonpk tinyint ,@modeupdcustprocpkonly tinyint ,@modecompensatingallkeys tinyint ,@modecompensatingnonpkkeys tinyint ,@modecompensatingpkonly tinyint ,@moderefreshcursordeclareallkeys tinyint ,@moderefreshcursordeclarenonpkkeys tinyint -- -- initialize -- select @modeinscustproc = 1 ,@modeupdcustprocnonpk = 2 ,@modeupdcustprocpkonly = 3 ,@modecompensatingallkeys = 4 ,@modecompensatingnonpkkeys = 5 ,@moderefreshcursordeclareallkeys = 6 ,@moderefreshcursordeclarenonpkkeys = 7 ,@modecompensatingpkonly = 8 -- -- security check - should be dbo or sysadmin -- exec @retcode = dbo.sp_MSreplcheck_publish if @@error != 0 or @retcode != 0 return (1) -- -- process if the object is a table and has index -- if (ObjectProperty(@tabid, 'IsTable') != 1) or (ObjectProperty(@tabid, 'TableHasIndex') != 1) return (1) -- -- Get the qualified name of the table -- exec @retcode = sp_MSget_qualified_name @tabid, @qualname OUTPUT if @@error != 0 or @retcode != 0 or @qualname is null return (1) -- -- @columns cannot be null -- if (@columns is null) return (1) -- -- Check @mode -- if (@mode not in (@modeinscustproc, @modeupdcustprocnonpk, @modeupdcustprocpkonly, @modecompensatingallkeys, @modecompensatingnonpkkeys, @moderefreshcursordeclareallkeys, @moderefreshcursordeclarenonpkkeys, @modecompensatingpkonly)) begin return (1) end -- -- validate @paramcount -- if ((@mode in (@modeupdcustprocnonpk,@modeupdcustprocpkonly)) and (@paramcount is null)) begin return (1) end -- -- enumerate indices -- The scripting will be done as follows : -- A) all keys will include PK and all unique keys -- where (pk1 = @cv and pk2 = @cw ...) or (ui1k1 = @cx and ui1k2 = @cy ...) or (u2k1 = @cz and ...) ... -- B) non PK keys will use only the unique keys that are not part of PK -- where (ui1k1 = @cx and ui1k2 = @cy ...) or (u2k1 = @cz and ...) ... -- select @cmd = case when (@mode in (@modecompensatingallkeys, @modecompensatingnonpkkeys, @modecompensatingpkonly)) then N' '' where ' else N' where ' end ,@findexstarted = 0 ,@fisfirstindex = 1 declare #hcindid cursor local fast_forward for select indid, status from sysindexes where id = @tabid and (status & 2) != 0 and indid > 0 and indid < 255 order by indid asc open #hcindid fetch #hcindid into @indid, @indstatus while (@@fetch_status != -1) begin -- -- If we are in (@modeupdcustprocnonpk, @modecompensatingnonpkkeys, -- @moderefreshcursordeclarenonpklkeys) mode then skip processing the PK index. -- If we are in (@modeupdcustprocpkonly, @modecompensatingpkonly) mode -- skip processing the non PK index -- if ((@mode in (@modeupdcustprocnonpk,@modecompensatingnonpkkeys, @moderefreshcursordeclarenonpkkeys)) and (@indstatus & 2048) != 0) or ((@mode in (@modeupdcustprocpkonly, @modecompensatingpkonly)) and (@indstatus & 2048) = 0) begin -- -- fetch next unique index -- fetch #hcindid into @indid, @indstatus continue end -- -- Enumerate the keys in this index -- select @indkey = 1 while (@indkey <= 16) begin -- -- get the column name for the key -- select @colname = index_col(@qualname, @indid, @indkey) if (@colname is null) break -- -- check if this column is enabled for replication -- select @artcol = 0 exec dbo.sp_MSget_col_position @tabid, @columns, @colname, NULL, @artcol output, 0, NULL, @thiscol output if (@artcol > 0) begin -- -- check if we are scripting the first key for this index -- if (@findexstarted = 1) begin select @cmd = case when (@mode in (@modecompensatingallkeys, @modecompensatingnonpkkeys, @modecompensatingpkonly)) then @cmd + N' + '' and ' else @cmd + N' and ' end end else begin -- -- check if we are scripting the first index. -- if (@fisfirstindex = 0) begin select @cmd = @cmd + N' or ' end else begin select @fisfirstindex = 0 end -- -- set @findexstarted while processing the first key -- select @findexstarted = 1 select @cmd = @cmd + N' ( ' end -- -- script this column -- if (@mode in (@modecompensatingallkeys, @modecompensatingnonpkkeys, @modecompensatingpkonly)) begin -- -- scripting a delete compensating command for synctran proc - we are building a dynamic string -- case when @c<this_col> is null then '[col<this_col>] is null' else '[col<this_col>] = @c<this_col>' end -- declare @ccoltype sysname select @var = @prefix + cast(@thiscol as nvarchar(10)) if (@suffix is not null) select @var = @var + @suffix select @cmd = @cmd + N''' + case when ( ' + @var + N' is null) then '' ' + quotename(@colname) + N' is null '' else '' ' + quotename(@colname) exec dbo.sp_MSget_colinfo @tabid, @thiscol, @columns, 0, NULL, @ccoltype output if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) = 'varchar' ) select @cmd = @cmd + N' = '''''' + CAST( master.dbo.fn_MSgensqescstr(' + @var + N') collate database_default as varchar) + '''''''' end ' else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) = 'nvarchar') select @cmd = @cmd + N' = N'''''' + master.dbo.fn_MSgensqescstr(' + @var + N') collate database_default + '''''''' end ' else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) = 'char') select @cmd = @cmd + N' = '''''' + master.dbo.fn_MSgensqescstr(CAST(RTRIM(' + @var + N') as nvarchar)) collate database_default + '''''''' end ' else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) = 'nchar') select @cmd = @cmd + N' = N'''''' + master.dbo.fn_MSgensqescstr(CAST(RTRIM(' + @var + N') as nvarchar)) collate database_default + '''''''' end ' else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('binary','varbinary')) select @cmd = @cmd + N' = '' + master.dbo.fn_varbintohexstr(' + @var + N') collate database_default end ' else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('bit','bigint','int','smallint','tinyint','decimal','numeric')) select @cmd = @cmd + N' = '' + CAST( ' + @var + N' as nvarchar) end ' else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('float','real')) select @cmd = @cmd + N' = '' + CONVERT(nvarchar(60), ' + @var + N' , 2) end ' else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('money','smallmoney')) select @cmd = @cmd + N' = '' + CONVERT(nvarchar(40), ' + @var + N' , 2) end ' else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) = 'uniqueidentifier') select @cmd = @cmd + N' = '''''' + CAST( ' + @var + N' as nvarchar(40)) + '''''''' end ' else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('datetime','smalldatetime')) select @cmd = @cmd + N' = '''''' + CONVERT(nvarchar(40), ' + @var + N', 112) + N'' '' + CONVERT(nvarchar(40), ' + @var + N', 114) + '''''''' end ' else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) = 'sql_variant') select @cmd = @cmd + N' = '' + master.dbo.fn_sqlvarbasetostr( ' + @var + N' ) collate database_default end ' else select @cmd = @cmd + N' = '' + CAST( ' + @var + N' as nvarchar) end ' end else begin -- -- scripting for custom procs - static script -- For publisher scripting - use @thiscol -- For custom proc scripting - use @artcol -- For Update custom proc scripting special cases - use variation of @artcol -- select @var = case when (@mode in (@modeupdcustprocnonpk,@modeupdcustprocpkonly)) then @prefix + cast((@artcol + @paramcount/2) as nvarchar(10)) when (@mode in (@moderefreshcursordeclareallkeys, @moderefreshcursordeclarenonpkkeys)) then @prefix + cast(@thiscol as nvarchar(10)) else @prefix + cast(@artcol as nvarchar(10)) end if (@suffix is not null) select @var = @var + @suffix -- -- Does the column allow NULLs -- if (columnproperty( @tabid , @colname , 'AllowsNull' ) = 1) begin -- -- static scripting should handle NULL valued column -- ((<@var> is null and <colname> is null) or (<@var> is not null and <colname> = <@var>)) -- select @cmd = @cmd + N'((' + @var + N' is null and ' + quotename(@colname) + N' is null) or (' + @var + N' is not null and ' + quotename(@colname) + N' = ' + @var + N'))' end else begin -- -- static scripting does not need to check for NULL values -- <colname> = <@var> -- select @cmd = @cmd + quotename(@colname) + N' = ' + @var end -- -- special processing for @modeupdcustprocnonpk -- if (@mode = @modeupdcustprocnonpk) begin select @cmd = @cmd + N' and ' + @prefix + cast((@artcol + @paramcount/2) as nvarchar(10)) if (@suffix is not null) select @cmd = @cmd + @suffix select @cmd = @cmd + N' != ' + @prefix + cast(@artcol as nvarchar(10)) if (@suffix is not null) select @cmd = @cmd + @suffix end end -- -- transfer command string to table if too large -- if (len(@cmd) > 3000) exec dbo.sp_MSflush_command @cmd output, 1, 0 end -- -- get the next key for the index -- select @indkey = @indkey + 1 end -- -- done with current index -- select @findexstarted = 0 ,@cmd = case when (@mode in (@modecompensatingallkeys, @modecompensatingnonpkkeys, @modecompensatingpkonly)) then @cmd + N' + '' ) ' else @cmd + N' ) ' end -- -- fetch next unique index -- fetch #hcindid into @indid, @indstatus end close #hcindid deallocate #hcindid -- -- Final flush -- if (@mode in (@modecompensatingallkeys, @modecompensatingnonpkkeys, @modecompensatingpkonly)) select @cmd = @cmd + N'''' if (len(@cmd) > 0) exec dbo.sp_MSflush_command @cmd output, 1, 0 -- -- all done -- return 0 end go exec sp_MS_marksystemobject sp_replscriptuniquekeywhereclause go -- ------------------------------------------------------------------ -- Fix final partial empty compensating command problem -- ------------------------------------------------------------------ -------------------------------------------------------------------------------- --. sp_MSadd_compensating_cmd -------------------------------------------------------------------------------- if exists (select * from sysobjects where type = 'P' and name = 'sp_MSadd_compensating_cmd') drop procedure sp_MSadd_compensating_cmd go raiserror('Creating procedure sp_MSadd_compensating_cmd', 0,1) go CREATE PROCEDURE sp_MSadd_compensating_cmd( @orig_srv sysname, @orig_db sysname, @command nvarchar(4000), @article_id int, @publication_id int, @cmdstate bit=0, @mode int=0, @setprefix bit=1 ) AS BEGIN set nocount on declare -- -- variable declarations for all modes -- @retcode int, @command_id int, -- command sequence @partial_cmd int, -- partial command flag @curlen int, -- current length to read @start_index int, -- index to start reading @max_fragment int, -- max binary fragment @full_command nvarchar(4000), -- qualified command @readsize int, -- read length chars or bytes based on mode @mode_postpublog int, @mode_insdistcmd int, -- -- variable declarations specific to mode = 1 -- @partial_cmdbit bit, -- partial command flag @xact_seqno varbinary(16), @publisher_id int, -- publisher ID @publisher_db sysname, -- publisher Db @distributor sysname, -- distribution server @distribdb sysname, -- distribution db @charsize int, -- char size @binary_cmd varbinary(1024), -- Binary converted command @distproc nvarchar(300) -- RPC string -- -- Security Check -- exec @retcode = dbo.sp_MSreplcheck_publish if ((@@ERROR != 0) or (@retcode != 0)) return(1) -- -- Initialize -- select @mode_postpublog = 0, @mode_insdistcmd = 1 if (@mode NOT in (@mode_postpublog,@mode_insdistcmd)) return(1) -- -- We will not post final partial empty(may contain space) command -- since logreader skips empty commands and this causes distribution -- agent to get confused when it selects the commands to read. -- If this partial command happens to be the final partial command which -- has a single space - then add a comment -- if ((len(@command) = 0) and (@cmdstate = 0)) select @command = N'/*c*/' -- -- process based on @mode -- if (@mode = @mode_insdistcmd) begin select @publisher_db = db_name(), @publisher_id = srvid from master.dbo.sysservers where UPPER(srvname) = UPPER(@@servername) collate database_default -- -- Get distribution server information for remote RPC calls -- EXECUTE @retcode = dbo.sp_helpdistributor @rpcsrvname = @distributor OUTPUT, @distribdb = @distribdb OUTPUT if ((@@ERROR != 0) or (@retcode != 0)) return(1) -- -- Get the new xact_seqno -- create table #new_xact_seqno ( seqno varbinary(16) NOT NULL ) select @distproc = RTRIM(@distributor) + '.' + @distribdb +'.dbo.sp_MSget_new_xact_seqno' insert into #new_xact_seqno EXECUTE @retcode = @distproc @publisher_id = @publisher_id, @publisher_db = @publisher_db, @len = 14 if ((@@ERROR != 0) or (@retcode != 0)) return(1) select @xact_seqno = seqno from #new_xact_seqno select @distproc = RTRIM(@distributor) + '.' + @distribdb +'.dbo.sp_MSadd_repl_command' end -- -- Do the command insertion in a tran -- select @full_command = case when (@setprefix = 1) then QUOTENAME(@orig_srv) + QUOTENAME(@orig_db) + @command else @command end begin tran sp_MSadd_compensating_cmd -- -- process the command -- for @mode_postpublog : just call sp_replpostcmd and that will do the job -- for @mode_insdistcmd : break the command into 1024 sized commands and add -- if (@mode = @mode_postpublog) begin select @partial_cmd = CASE when (@cmdstate = 1) then 1 else 0 END exec @retcode = dbo.sp_replpostcmd @partial_cmd, @publication_id, @article_id, 12, @full_command if (@@ERROR != 0 or @retcode != 0) GOTO UNDO end else if (@mode = @mode_insdistcmd) begin select @command_id = 0, @start_index = 1, @max_fragment = 1024, @charsize = 2, @curlen = LEN(@full_command), @readsize = DATALENGTH(@full_command) while (@readsize > 0) begin -- set command id select @command_id = @command_id + 1 -- Check if we have to process partial command if (@readsize > @max_fragment) begin -- -- we have partial command to send -- select @curlen = @max_fragment / @charsize select @partial_cmdbit = 1, @binary_cmd = CAST( SUBSTRING(@full_command, @start_index, @curlen) AS varbinary(1024)), @readsize = @readsize - @max_fragment select @start_index = @start_index + @curlen select @curlen = @readsize / @charsize end else begin -- -- last fragment to send - end of command -- check for command state - if state is PARTIAL_CMD (1) -- then set the partial bit even though this is the last fragment -- select @partial_cmdbit = CASE when (@cmdstate = 1) then 1 else 0 END, @binary_cmd = CAST( SUBSTRING(@full_command, @start_index, @curlen) AS varbinary(1024)), @readsize = 0 end -- -- Add the command to the distributor -- EXECUTE @retcode = @distproc @publisher_id = @publisher_id, @publisher_db = @publisher_db, @xact_seqno = @xact_seqno, @type = 12, @article_id = @article_id, @command_id = @command_id, @partial_command = @partial_cmdbit, @command = @binary_cmd if (@@ERROR != 0 or @retcode != 0) GOTO UNDO end -- end of while loop end -- end of if (@mode = @mode_insdistcmd) -- -- Command(s) added successfully - End Tran -- commit tran sp_MSadd_compensating_cmd return (0) UNDO: -- -- Error - Rollback -- IF (@@TRANCOUNT > 0) begin ROLLBACK TRAN sp_MSadd_compensating_cmd if (@@TRANCOUNT > 0) COMMIT TRAN end return (1) END go EXEC dbo.sp_MS_marksystemobject sp_MSadd_compensating_cmd GO grant execute on dbo.sp_MSadd_compensating_cmd to public go -------------------------------------------------------------------------------- --. sp_MSscript_where_clause -------------------------------------------------------------------------------- if exists (select * from sysobjects where type = 'P' and name = 'sp_MSscript_where_clause') drop procedure sp_MSscript_where_clause go raiserror('Creating procedure sp_MSscript_where_clause', 0,1) go create procedure sp_MSscript_where_clause ( @objid int, @columns binary(32), @clause_type varchar(15) = 'pk_new', -- 'new pk', 'old pk', 'upd version', 'upd rc', 'trg pk', 'qcft_comp', 'new_pk_q', 'subwins_check' @ts_col sysname = NULL, @indent int = 0, @op_type char(3) = NULL, -- 'ins', 'del', 'upd' @primary_key_bitmap varbinary(4000) = null ) as BEGIN declare @cmd nvarchar(4000) ,@colname sysname ,@ccoltype sysname ,@spacer nvarchar(20) ,@indkey int ,@indid int ,@key sysname ,@rc int ,@this_col int ,@art_col int ,@src_cols int ,@total_col int ,@col sysname ,@qualname nvarchar(512) ,@curparam nvarchar(20) ,@retcode int ,@fcreatedcolmap bit declare @colmap table (relativeorder int identity(1,1), colid int) select @spacer = N' ' ,@cmd = N'' ,@indkey = 1 ,@indid = 0 ,@fcreatedcolmap = 0 exec sp_MSget_qualified_name @objid, @qualname OUTPUT select @src_cols = max(colid) ,@total_col = count(colid) from syscolumns where id = @objid exec dbo.sp_MSpad_command @cmd output, @indent if (@clause_type = 'qcft_comp') select @cmd = @cmd + N' '' where ' else select @cmd = @cmd + N'where' exec dbo.sp_MSflush_command @cmd output, 1, @indent if @clause_type in ('new pk','old pk','upd version','trg pk','version pk','qcft_comp','new_pk_q','subwins_check') begin if @primary_key_bitmap is null begin exec @indid = dbo.sp_MStable_has_unique_index @objid if @indid is null begin raiserror('Debug: Cannot find unique index', 16, -1) return (1) end end -- -- check if column Id match relative column order -- for specific trigger operations -- if ((@total_col < @src_cols) and (@clause_type = 'trg pk') and (@columns is null) and (@primary_key_bitmap is not null)) begin -- -- this table may have altered columns, so when we need to -- set a mapping for using the bitmaps properly as the bitmap -- always refers relative column order -- insert into @colmap (colid) select colid from syscolumns where id = @objid order by colid if (@@error != 0) begin raiserror('Could not create column mapping', 16, -1) return (1) end select @fcreatedcolmap = 1 end while (1=1) begin -- -- get the column position -- if @primary_key_bitmap is null begin select @key = index_col(@qualname, @indid, @indkey) if @key is null break --exec dbo.sp_MSget_col_position @objid, @columns, @key, @col output, @this_col output exec dbo.sp_MSget_col_position @objid, @columns, @key, @col output, NULL, 0, NULL, @this_col output end else begin exec dbo.sp_MSget_map_position @primary_key_bitmap, @indkey, @col output, @this_col output if @this_col is null break -- -- set the actual column id for this relative order in the PK bitmap if necessary -- if (@fcreatedcolmap = 1) begin select @this_col = colid ,@col = 'c' + convert(sysname, colid) from @colmap where relativeorder = @this_col end end -- -- Get column name -- exec @retcode = dbo.sp_MSget_colinfo @objid, @this_col, @columns, 0, @key output, @ccoltype output if (@retcode = 1) begin -- -- this column not used for replication -- continue -- select @indkey = @indkey + 1 continue end if @clause_type = 'new pk' begin if ColumnProperty(@objid, @key, 'IsIdentity') = 1 select @cmd = @cmd + @spacer + quotename(@key) + N' = @@identity' else select @cmd = @cmd + @spacer + quotename(@key) + N' = @' + @col select @spacer = ' and ' end else if @clause_type in ('upd version', 'subwins_check') begin select @cmd = @cmd + @spacer + quotename(@key) + N' = @' + @col + N'_old' select @spacer = ' and ' end else if @clause_type = 'version pk' begin select @cmd = @cmd + @spacer + @qualname + '.' + quotename(@key) + N' = inserted.' + quotename(@key) select @spacer = ' and ' end else if @clause_type in ('trg pk', 'old pk') begin if @op_type = 'ins' select @cmd = @cmd + @spacer + quotename(@key) + N' = @' + @col + N'_old' else -- The vars correspoding to pk were set in sp_MSscript -- _pkvar_assignment. select @cmd = @cmd + @spacer + quotename(@key) + N' = @' + @col select @spacer = ' and ' end else if (@clause_type = 'qcft_comp') begin -- -- Conflict compensation generation -- This is a special case - we generate -- and exec string for the WHERE clause -- if (@op_type = 'ins') select @curparam = N'@' + @col else if (@op_type = 'del') select @curparam = N'@' + @col + N'_old' else select @curparam = N'ISNULL(@' + @col + N', @' + @col + N'_old)' select @cmd = @cmd + @spacer + quotename(@key) if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) = 'varchar') select @cmd = @cmd + N' = '' + '''''''' + master.dbo.fn_MSgensqescstr(' + @curparam + N') collate database_default + '''''''' ' else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) = 'nvarchar') select @cmd = @cmd + N' = '' + ''N'''''' + master.dbo.fn_MSgensqescstr(' + @curparam + N') collate database_default + '''''''' ' else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) = 'char') select @cmd = @cmd + N' = '' + '''''''' + master.dbo.fn_MSgensqescstr(CAST(RTRIM(' + @curparam + N') as nvarchar)) collate database_default + '''''''' ' else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) = 'nchar') select @cmd = @cmd + N' = '' + ''N'''''' + master.dbo.fn_MSgensqescstr(CAST(RTRIM(' + @curparam + N') as nvarchar)) collate database_default + '''''''' ' else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('binary','varbinary')) select @cmd = @cmd + N' = '' + master.dbo.fn_varbintohexstr(' + @curparam + N') collate database_default ' else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('bit','bigint','int','smallint','tinyint','decimal','numeric')) select @cmd = @cmd + N' = '' + CAST(' + @curparam + N' as nvarchar) ' else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('float','real')) select @cmd = @cmd + N' = '' + CONVERT(nvarchar(60),' + @curparam + N', 2) ' else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('money','smallmoney')) select @cmd = @cmd + N' = '' + CONVERT(nvarchar(40),' + @curparam + N', 2) ' else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) = 'uniqueidentifier') select @cmd = @cmd + N' = '' + '''''''' + CAST(' + @curparam + N' as nvarchar(40)) + '''''''' ' else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('datetime','smalldatetime')) select @cmd = @cmd + N' = '' + '''''''' + CONVERT(nvarchar(40), ' + @curparam + N', 112) + N'' '' + CONVERT(nvarchar(40), ' + @curparam + N', 114) + '''''''' ' else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) = 'sql_variant') select @cmd = @cmd + N' = '' + master.dbo.fn_sqlvarbasetostr(' + @curparam + N' ) collate database_default ' else select @cmd = @cmd + N' = '' + CAST(' + @curparam + N' as nvarchar) ' select @spacer = ' + '' and ' end else if @clause_type = 'new_pk_q' begin -- -- new value of primary key, ignore identity -- used for scripting in synctran procs -- select @cmd = @cmd + @spacer + quotename(@key) + N' = @' + @col select @spacer = ' and ' end select @indkey = @indkey + 1 -- flush command if necessary exec dbo.sp_MSflush_command @cmd output, 1, @indent end -- end of while loop -- add version col as necessary if ((@clause_type in ('upd version','subwins_check')) and (@ts_col is not null)) begin -- -- @ts_col is version col actually. -- check for special cases for queued processing -- exec dbo.sp_MSget_col_position @objid, @columns, @ts_col, @col output if (@clause_type = 'subwins_check') select @cmd = @cmd + @spacer + @ts_col + N' = @' + @col else select @cmd = @cmd + @spacer + @ts_col + N' = @' + @col + N'_old' -- -- save off command fragment -- exec dbo.sp_MSflush_command @cmd output, 1, @indent end end -- end of if clause_type -- 'upd rc' is used for column value conflict detection. It is no longer used. else if @clause_type = 'upd rc' begin select @this_col = 1, @art_col = 1 while @this_col <= @src_cols begin exec @rc = dbo.sp_MSget_colinfo @objid, @this_col, @columns, 0, @colname output, @ccoltype output if @rc = 0 begin select @cmd = @cmd + @spacer + '(' + @colname + N' = @c' + convert(varchar(4), @this_col) + N'_old or (' select @cmd = @cmd + @colname + ' is null and @c' + convert(varchar(4), @this_col) + N'_old is null)) ' select @spacer = N' and ' exec dbo.sp_MSflush_command @cmd output, 0, @indent end exec dbo.sp_MSflush_command @cmd output, 1, @indent select @this_col = @this_col + 1 end -- save off cmd fragment exec dbo.sp_MSflush_command @cmd output, 1, @indent end END go EXEC dbo.sp_MS_marksystemobject sp_MSscript_where_clause GO -------------------------------------------------------------------------------- --. sp_MSmaketrancftproc -------------------------------------------------------------------------------- if exists (select * from sysobjects where type = 'P' and name = 'sp_MSmaketrancftproc') drop procedure sp_MSmaketrancftproc go raiserror('Creating procedure sp_MSmaketrancftproc', 0,1) go create procedure sp_MSmaketrancftproc ( @article sysname, @publication sysname, @is_debug bit=0) as BEGIN declare @source_table nvarchar(540) ,@owner sysname ,@procname sysname ,@source_objid int ,@artid int ,@pubid int ,@conflict_tableid int ,@conflict_table sysname ,@conflict_proc_id int ,@indid int ,@indkey int ,@ind_col_name sysname ,@qualname nvarchar(540) ,@destqualname nvarchar(540) ,@destowner sysname ,@dbname sysname ,@retcode smallint ,@retain_varname int declare @colid int ,@colname sysname ,@coltype sysname ,@ccoltype sysname ,@rowcnt int declare @argtabempty bit ,@seltabempty bit ,@sel2tabempty bit ,@valtabempty bit ,@paramtabempty bit ,@where_clausetabempty bit ,@decltabempty bit ,@assigntabempty bit ,@compinsertabempty bit declare @argterm nvarchar(4000) ,@selterm nvarchar(4000) ,@sel2term nvarchar(4000) ,@updterm nvarchar(4000) ,@valterm nvarchar(4000) ,@paramterm nvarchar(4000) ,@where_term nvarchar(4000) ,@declterm nvarchar(4000) ,@assignterm nvarchar(4000) ,@compinsterm nvarchar(4000) declare @cmd nvarchar(4000) set nocount on -- -- prepare the proc name and get the other parameters -- select @artid = a.artid, @pubid = a.pubid, @source_table = object_name(a.objid), @source_objid = a.objid, @destowner = a.dest_owner from sysarticles a, syspublications p where a.name = @article and p.name = @publication and a.pubid = p.pubid -- Get the object owner name select @owner = u.name from sysusers u, sysobjects o where o.id = @source_objid and o.uid = u.uid -- -- Prepare the proc name -- The source table should be owner qualified -- select @source_table = QUOTENAME(@owner) + N'.' + QUOTENAME(@source_table) exec @retcode = sp_MSgettranconflictname @publication=@publication, @source_object= @source_table, @str_prefix='sp_MScft_', @conflict_table=@procname OUTPUT -- -- The conflict table should exist before we do any conflict procs -- select @conflict_tableid = conflict_tableid, @conflict_table = OBJECT_NAME(conflict_tableid) from sysarticleupdates where artid = @artid and pubid = @pubid if ( @conflict_tableid is NULL) return (1) -- -- To check if specified object exists in current database -- select @qualname = case when (@owner is null or @owner = ' ') then QUOTENAME(@conflict_table) else QUOTENAME(@owner) + N'.' + QUOTENAME(@conflict_table) end if (object_id(@qualname) is NULL) return (1) -- -- The source table should have an unique index -- exec @indid = dbo.sp_MStable_has_unique_index @source_objid if (@indid = 0) return (1) -- -- Get all the columns participating in the index of the source table -- create table #indcoltab ( colname sysname collate database_default ) select @indkey = 1; while (@indkey <= 16) begin select @ind_col_name = index_col(@source_table, @indid, @indkey) if (@ind_col_name is not NULL) insert into #indcoltab(colname) values(@ind_col_name) else select @indkey = 16 select @indkey = @indkey + 1 end -- -- prepare destination table name (required for decentralized conflict processing) -- select @destqualname = case when (@destowner is null or @destowner = ' ') then QUOTENAME(@conflict_table) else QUOTENAME(@destowner) + N'.' + QUOTENAME(@conflict_table) end -- build the lists select @argtabempty = 1 ,@valtabempty = 1 ,@paramtabempty = 1 ,@seltabempty = 1 ,@sel2tabempty = 1 ,@decltabempty = 1 ,@assigntabempty = 1 ,@where_clausetabempty = 1 ,@compinsertabempty = 1 create table #argtab ( c1 int identity NOT NULL, procedure_text nvarchar(4000) collate database_default null) create table #valtab ( c1 int identity NOT NULL, procedure_text nvarchar(4000) collate database_default null) create table #paramtab ( c1 int identity NOT NULL, procedure_text nvarchar(4000) collate database_default null) create table #seltab ( c1 int identity NOT NULL, procedure_text nvarchar(4000) collate database_default null) create table #sel2tab ( c1 int identity NOT NULL, procedure_text nvarchar(4000) collate database_default null) create table #decltab ( c1 int identity NOT NULL, procedure_text nvarchar(4000) collate database_default null) create table #assigntab ( c1 int identity NOT NULL, procedure_text nvarchar(4000) collate database_default null) create table #where_clausetab ( c1 int identity NOT NULL, procedure_text nvarchar(4000) collate database_default null) create table #compinstab ( c1 int identity NOT NULL, procedure_text nvarchar(4000) collate database_default null) -- some predefined declares and assignments select @cmd = N' declare @reinit_code int, @subwins_code int, @pubwins_code int, @qcfttabrowid uniqueidentifier ,@retcode smallint, @compcmd nvarchar(4000), @centralized_conflicts bit' insert into #decltab(procedure_text) values(@cmd) select @decltabempty = 0 select @cmd = N' select @reinit_code = 3 ,@subwins_code = 2 ,@pubwins_code = 1 ,@qcfttabrowid = NEWID()' insert into #assigntab(procedure_text) values(@cmd) select @cmd = N' select @centralized_conflicts = centralized_conflicts from dbo.syspublications where pubid = ' + cast(@pubid as nvarchar) insert into #assigntab(procedure_text) values(@cmd) select @assigntabempty = 0 declare #argcursor cursor local FAST_FORWARD FOR select colid from syscolumns where iscomputed = 0 and id=@conflict_tableid order by colid FOR READ ONLY select @retain_varname = 0 open #argcursor fetch #argcursor into @colid while (@@FETCH_STATUS = 0) begin -- -- Get the column name and column type -- exec dbo.sp_MSget_type @conflict_tableid, @colid, @colname output, @coltype OUTPUT if (@@ERROR<>0 or @retcode<>0) return (1) -- -- skip this specific column or if type is not returned -- if ((@coltype IS NULL) or (LOWER(@colname) = 'qcfttabrowid')) begin -- do the next fetch and continue fetch #argcursor into @colid continue end exec dbo.sp_MSget_colinfo @conflict_tableid, @colid, NULL, 0, NULL, @ccoltype output if (@@ERROR<>0 or @retcode<>0) return (1) -- -- parameterize the vars that are the column values of the source -- table. For the columns that are specific to the conflict table -- retain specific names for the vars -- if (LOWER(@colname collate SQL_Latin1_General_CP1_CS_AS) = 'origin_datasource') select @retain_varname = @colid if (@retain_varname = 0) select @argterm = N'@param' + cast(@colid as nvarchar) else select @argterm = N'@' + @colname select @valterm = quotename(@colname) select @paramterm = @argterm select @updterm = @valterm + N' = ' + @argterm if (@retain_varname = 0) begin select @selterm = @paramterm + N' = ' + @valterm select @sel2term = @paramterm + N' = case when ' + @paramterm + N' is NULL then ' + @valterm + N' else ' + @paramterm + N' end' end else begin select @selterm = NULL select @sel2term = NULL end select @argterm = @argterm + N' ' + @coltype -- Check if this is part of primary key / unique index if (@colname in ( select colname from #indcoltab ) ) begin -- this key assignment becomes part of where clause select @where_term = @updterm select @updterm = NULL select @selterm = NULL select @sel2term = NULL end else select @where_term = NULL -- special columns - process them as local var if (LOWER(@colname collate SQL_Latin1_General_CP1_CS_AS) = 'insertdate' ) begin select @declterm = N' declare ' + @argterm select @assignterm = N' select ' + @paramterm + N' = GETDATE()' select @argterm = NULL end else if (LOWER(@colname collate SQL_Latin1_General_CP1_CS_AS) = 'pubid' ) begin select @declterm = N' declare ' + @argterm select @assignterm = N' select ' + @paramterm + N' = ' + cast(@pubid as nvarchar) select @argterm = NULL end else begin select @declterm = NULL select @assignterm = NULL end -- build the term for compensating insert if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) = 'varchar') select @compinsterm = N' '''''' + master.dbo.fn_MSgensqescstr(' + @valterm + N') collate database_default + '''''''' ' else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) = 'nvarchar') select @compinsterm = N' N'''''' + master.dbo.fn_MSgensqescstr(' + @valterm + N') collate database_default + '''''''' ' else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) = 'char') select @compinsterm = N' '''''' + master.dbo.fn_MSgensqescstr(CAST(RTRIM(' + @valterm + N') as nvarchar(4000))) collate database_default + '''''''' ' else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) = 'nchar') select @compinsterm = N' N'''''' + master.dbo.fn_MSgensqescstr(CAST(RTRIM(' + @valterm + N') as nvarchar(4000))) collate database_default + '''''''' ' else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('binary','varbinary')) select @compinsterm = N' '' + master.dbo.fn_varbintohexstr(' + @valterm + N') collate database_default ' else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('bit','bigint','int','smallint','tinyint','decimal','numeric')) select @compinsterm = N' '' + CAST(' + @valterm + N' as nvarchar) ' else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('float','real')) select @compinsterm = N' '' + CONVERT(nvarchar(60),' + @valterm + N', 2) ' else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('money','smallmoney')) select @compinsterm = N' '' + CONVERT(nvarchar(40),' + @valterm + N', 2) ' else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) = 'uniqueidentifier') select @compinsterm = N' '''''' + CAST(' + @valterm + N' as nvarchar(40)) + '''''''' ' else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('datetime','smalldatetime')) select @compinsterm = N' '''''' + CONVERT(nvarchar(40), ' + @valterm + N', 112) + N'' '' + CONVERT(nvarchar(40), ' + @valterm + N', 114) + '''''''' ' else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) = 'sql_variant') select @compinsterm = N' '' + master.dbo.fn_sqlvarbasetostr(' + @valterm + N' ) collate database_default ' else select @compinsterm = N' '' + CAST(' + @valterm + N' as nvarchar) ' -- Now append to the various lists if (@argterm is NOT NULL) begin if (@argtabempty = 1) begin select @argtabempty = 0 select @cmd = N' ' + @argterm end else select @cmd = N', ' + @argterm insert into #argtab(procedure_text) values(@cmd) end if (@valterm is NOT NULL) begin if (@valtabempty = 1) begin select @valtabempty = 0 select @cmd = @valterm end else select @cmd = N', ' + @valterm insert into #valtab(procedure_text) values(@cmd) end if (@paramterm is NOT NULL) begin if (@paramtabempty = 1) begin select @paramtabempty = 0 select @cmd = @paramterm end else select @cmd = N', ' + @paramterm insert into #paramtab(procedure_text) values(@cmd) end if (@selterm is NOT NULL) begin if (@seltabempty = 1) begin select @seltabempty = 0 select @cmd = N' ' + @selterm end else select @cmd = N' ,' + @selterm insert into #seltab(procedure_text) values(@cmd) end if (@sel2term is NOT NULL) begin if (@sel2tabempty = 1) begin select @sel2tabempty = 0 select @cmd = N' ' + @sel2term end else select @cmd = N' ,' + @sel2term insert into #sel2tab(procedure_text) values(@cmd) end if (@where_term is NOT NULL) begin if (@where_clausetabempty = 1) begin select @where_clausetabempty = 0 select @cmd = @where_term end else select @cmd = N' AND ' + @where_term insert into #where_clausetab(procedure_text) values(@cmd) end if (@declterm is NOT NULL) begin select @cmd = @declterm + N' ' insert into #decltab(procedure_text) values(@cmd) end if (@assignterm is NOT NULL) begin select @cmd = @assignterm + N' ' insert into #assigntab(procedure_text) values(@cmd) end if (@compinsterm is NOT NULL) begin if (@compinsertabempty = 1) begin select @compinsertabempty = 0 select @cmd = N' + ISNULL(''' + @compinsterm + N', ''null'')' end else select @cmd = N' + ISNULL('',' + @compinsterm + N', ''null'')' insert into #compinstab(procedure_text) values(@cmd) end -- do the next fetch fetch #argcursor into @colid end close #argcursor deallocate #argcursor drop table #indcoltab -- -- generation phase -- BEGIN TRAN sp_MSmaketrancftproc -- create temp table to select the command text out of if exists (select * from sysobjects where name = 'tempcmd' and uid = user_id('dbo')) drop table dbo.tempcmd create table dbo.tempcmd ( c1 int identity NOT NULL, cmdtext nvarchar(4000) NULL) -- create header insert into dbo.tempcmd(cmdtext) values(N'create procedure '+QUOTENAME(@owner)+ N'.'+ QUOTENAME(@procname) + N'( ') -- insert the arglist insert into dbo.tempcmd(cmdtext) select procedure_text from #argtab order by c1 insert into dbo.tempcmd(cmdtext) values(N' ,@subcriber sysname = NULL, @subdb sysname = NULL ) as begin ') -- insert the declare list insert into dbo.tempcmd(cmdtext) select procedure_text from #decltab order by c1 insert into dbo.tempcmd(cmdtext) values(N' ') -- insert the assignment list (for declared vars) insert into dbo.tempcmd(cmdtext) select procedure_text from #assigntab order by c1 -- do the select for the case where we need to retain values of publisher insert into dbo.tempcmd(cmdtext) values(N' if (@reason_code = @subwins_code) begin select ') insert into dbo.tempcmd(cmdtext) select procedure_text from #seltab order by c1 insert into dbo.tempcmd(cmdtext) values(N' from ' + @source_table + N' where ') insert into dbo.tempcmd(cmdtext) select procedure_text from #where_clausetab order by c1 insert into dbo.tempcmd(cmdtext) values(N' end') insert into dbo.tempcmd(cmdtext) values(N' else begin select ') insert into dbo.tempcmd(cmdtext) select procedure_text from #sel2tab order by c1 insert into dbo.tempcmd(cmdtext) values(N' from ' + @source_table + N' where ') insert into dbo.tempcmd(cmdtext) select procedure_text from #where_clausetab order by c1 insert into dbo.tempcmd(cmdtext) values(N' end ') -- -- insert the conflict row in the publisher cft table -- insert into dbo.tempcmd(cmdtext) values(N' insert into ' + @qualname + N'(') insert into dbo.tempcmd(cmdtext) select procedure_text from #valtab order by c1 insert into dbo.tempcmd(cmdtext) values(N',[qcfttabrowid]) values (') insert into dbo.tempcmd(cmdtext) select procedure_text from #paramtab order by c1 insert into dbo.tempcmd(cmdtext) values(N',@qcfttabrowid) ') -- -- generate compensating command decentralized logging -- depending on the number of columns, we will split the compensating -- command into several compensating commands -- select @rowcnt = 0, @compinsertabempty = 1 select @cmd = N' if (@centralized_conflicts = 0) begin select @compcmd = N''insert into ' + master.dbo.fn_MSgensqescstr(@destqualname) collate database_default + N' ( ' insert into dbo.tempcmd(cmdtext) values(@cmd) declare #htempcur cursor local for select master.dbo.fn_MSgensqescstr(procedure_text) from #valtab order by c1 for read only open #htempcur fetch #htempcur into @compinsterm while (@@fetch_status = 0) begin insert into dbo.tempcmd(cmdtext) select @compinsterm select @rowcnt = @rowcnt + 1 -- -- if we have processed 10 terms then split the command -- if (@rowcnt > 9) begin select @cmd = N''' from ' + @qualname + N' where qcfttabrowid = @qcfttabrowid and tranid = @tranid' insert into dbo.tempcmd(cmdtext) values(@cmd) select @cmd = N' exec @retcode = dbo.sp_MSadd_compensating_cmd @subcriber, @subdb, @compcmd, ' + CAST(@artid as nvarchar(10)) + N', ' + CAST(@pubid as nvarchar(10)) + N',1,0,' + CAST(@compinsertabempty as nvarchar(4)) + N' if (@@error != 0 or @retcode != 0) return 1 select @compcmd = N''' insert into dbo.tempcmd(cmdtext) values(@cmd) select @rowcnt = 0, @compinsertabempty = 0 end fetch #htempcur into @compinsterm end close #htempcur deallocate #htempcur insert into dbo.tempcmd(cmdtext) values(N', [qcfttabrowid] ) values ('' ') select @rowcnt = @rowcnt + 1 declare #htempcur cursor local for select procedure_text from #compinstab order by c1 for read only open #htempcur fetch #htempcur into @compinsterm while (@@fetch_status = 0) begin insert into dbo.tempcmd(cmdtext) select @compinsterm select @rowcnt = @rowcnt + 1 -- -- if we have processed 10 terms then split the command -- if (@rowcnt > 9) begin select @cmd = N' from ' + @qualname + N' where qcfttabrowid = @qcfttabrowid and tranid = @tranid' insert into dbo.tempcmd(cmdtext) values(@cmd) select @cmd = N' exec @retcode = dbo.sp_MSadd_compensating_cmd @subcriber, @subdb, @compcmd, ' + CAST(@artid as nvarchar(10)) + N', ' + CAST(@pubid as nvarchar(10)) + N',1,0,' + CAST(@compinsertabempty as nvarchar(4)) + N' if (@@error != 0 or @retcode != 0) return 1 select @compcmd = N'' ''' insert into dbo.tempcmd(cmdtext) values(@cmd) select @rowcnt = 0, @compinsertabempty = 0 end fetch #htempcur into @compinsterm end close #htempcur deallocate #htempcur -- -- script the remaining compensating command -- select @cmd = N' + '', '''''' + CAST([qcfttabrowid] as nvarchar(40)) + '''''''' + N'' ) '' from ' + @qualname + N' where qcfttabrowid = @qcfttabrowid and tranid = @tranid' insert into dbo.tempcmd(cmdtext) values(@cmd) select @rowcnt = @rowcnt + 1 select @cmd = N' exec @retcode = dbo.sp_MSadd_compensating_cmd @subcriber, @subdb, @compcmd, ' + CAST(@artid as nvarchar(10)) + N', ' + CAST(@pubid as nvarchar(10)) + N',0,0,' + CAST(@compinsertabempty as nvarchar(4)) + N' if (@@error != 0 or @retcode != 0) return 1 ' insert into dbo.tempcmd(cmdtext) values(@cmd) insert into dbo.tempcmd(cmdtext) values(N' end end') if (@is_debug = 0) begin -- Now we select out the command text pieces in proper order so that our caller, -- xp_execresultset will execute the command that creates the stored procedure. select @dbname = db_name() select @cmd = N'select cmdtext from dbo.tempcmd order by c1' exec @retcode = master..xp_execresultset @cmd, @dbname if (@@error != 0 or @retcode != 0) begin -- roll back the tran rollback tran sp_MSmaketrancftproc return (1) end -- -- Check if we create the proc and update sysarticleupdates -- select @conflict_proc_id = id from sysobjects where name = @procname and type = 'P ' if (@conflict_proc_id is NULL or @conflict_proc_id = 0) begin -- roll back the tran rollback tran sp_MSmaketrancftproc return (1) end else begin update dbo.sysarticleupdates set ins_conflict_proc = @conflict_proc_id where artid = @artid and pubid = @pubid if @@error <> 0 begin -- roll back the tran rollback tran sp_MSmaketrancftproc return (1) end -- mark the proc as system object if (@owner in ('dbo','INFORMATION_SCHEMA')) begin exec @retcode = dbo.sp_MS_marksystemobject @procname if (@@error != 0 or @retcode != 0) begin -- roll back the tran rollback tran sp_MSmaketrancftproc return (1) end end end end else select cmdtext from dbo.tempcmd order by c1 COMMIT TRAN -- drop the temp tables drop table dbo.tempcmd drop table #argtab drop table #valtab drop table #paramtab drop table #seltab drop table #sel2tab drop table #decltab drop table #assigntab drop table #where_clausetab drop table #compinstab END go exec dbo.sp_MS_marksystemobject sp_MSmaketrancftproc go -------------------------------------------------------------------------------- --. fn_sqlvarbasetostr -------------------------------------------------------------------------------- if exists (select * from sysobjects where type = 'FN' and name = 'fn_sqlvarbasetostr') drop function fn_sqlvarbasetostr go raiserror('Creating function fn_sqlvarbasetostr', 0,1) go create function dbo.fn_sqlvarbasetostr ( @ssvar sql_variant ) returns nvarchar(4000) as begin declare @pstrout nvarchar(4000) ,@basetype sysname select @basetype = CAST(SQL_VARIANT_PROPERTY ( @ssvar, 'BaseType' ) as nvarchar(255)) if (@ssvar IS NOT NULL and @basetype IS NOT NULL) begin if (lower(@basetype collate SQL_Latin1_General_CP1_CS_AS) = 'varchar') select @pstrout = N'''' + REPLACE(CAST(@ssvar as nvarchar(4000)), '''', '''''') + N'''' else if (lower(@basetype collate SQL_Latin1_General_CP1_CS_AS) = 'nvarchar') select @pstrout = N'N''' + REPLACE(CAST(@ssvar as nvarchar(4000)), '''', '''''') + N'''' else if (lower(@basetype collate SQL_Latin1_General_CP1_CS_AS) = 'char') select @pstrout = N'''' + REPLACE(RTRIM(CAST(@ssvar as nvarchar(4000))), '''', '''''') + N'''' else if (lower(@basetype collate SQL_Latin1_General_CP1_CS_AS) = 'nchar') select @pstrout = N'N''' + REPLACE(RTRIM(CAST(@ssvar as nvarchar(4000))), '''', '''''') + N'''' else if (lower(@basetype collate SQL_Latin1_General_CP1_CS_AS) in ('binary','varbinary')) select @pstrout = master.dbo.fn_varbintohexsubstring(1, CAST(@ssvar as varbinary(8000)), 1, 0) else if (lower(@basetype collate SQL_Latin1_General_CP1_CS_AS) in ('bit','bigint','int','smallint','tinyint','decimal','numeric')) select @pstrout = CAST(@ssvar as nvarchar(40)) else if (lower(@basetype collate SQL_Latin1_General_CP1_CS_AS) in ('float','real')) select @pstrout = CONVERT(nvarchar(60), @ssvar, 2) else if (lower(@basetype collate SQL_Latin1_General_CP1_CS_AS) in ('money','smallmoney')) select @pstrout = CONVERT(nvarchar(40), @ssvar, 2) else if (lower(@basetype collate SQL_Latin1_General_CP1_CS_AS) = 'uniqueidentifier') select @pstrout = N'''' + CAST(@ssvar as nvarchar(40)) + N'''' else if (lower(@basetype collate SQL_Latin1_General_CP1_CS_AS) in ('datetime','smalldatetime')) select @pstrout = N'''' + CONVERT(nvarchar(40), @ssvar, 112) + N' ' + CONVERT(nvarchar(40), @ssvar, 114) + N'''' else select @pstrout = N'''Invalid Datatype' + lower(@basetype collate SQL_Latin1_General_CP1_CS_AS) + N'(' + CAST(@ssvar as nvarchar) + N')''' end -- All done return @pstrout end go exec dbo.sp_MS_marksystemobject fn_sqlvarbasetostr go grant execute on dbo.fn_sqlvarbasetostr to public go -------------------------------------------------------------------------------- --. sp_MSget_col_position -------------------------------------------------------------------------------- if exists (select * from sysobjects where type = 'P' and name = 'sp_MSget_col_position') drop procedure sp_MSget_col_position go raiserror('Creating procedure sp_MSget_col_position ', 0,1) go create procedure sp_MSget_col_position ( @objid int, @columns binary(32), @key sysname, @colpos sysname = NULL output, @art_col int = NULL output, @get_num_col bit = 0, @num_col int = NULL output, @this_col int = null output ) as BEGIN declare @colname sysname ,@ccoltype sysname ,@rc int select @num_col = 0 -- -- Use a cursor to walk through the columns -- DECLARE #hCColid CURSOR LOCAL FAST_FORWARD FOR select colid from syscolumns where id = @objid order by colid asc OPEN #hCColid FETCH #hCColid INTO @this_col WHILE (@@fetch_status <> -1) begin exec @rc = dbo.sp_MSget_colinfo @objid, @this_col, @columns, 1, @colname output, @ccoltype output if @rc = 0 begin select @num_col = @num_col + 1 -- If @get_num_col is 1, we just need the number of columns in the partition. if (@get_num_col != 1) and (@colname = @key) begin select @colpos = 'c' + convert(varchar(4), @this_col) ,@art_col = @num_col break end end FETCH #hCColid INTO @this_col end close #hCColid deallocate #hCColid return 0 END go exec dbo.sp_MS_marksystemobject sp_MSget_col_position go -------------------------------------------------------------------------------- --. sp_scriptpkwhereclause -------------------------------------------------------------------------------- if exists (select * from sysobjects where type = 'P' and name = 'sp_scriptpkwhereclause') drop procedure sp_scriptpkwhereclause go raiserror('Creating procedure sp_scriptpkwhereclause', 0,1) go create procedure sp_scriptpkwhereclause ( @src_objid int, @pkcolumns binary(32), @prefix nvarchar(10) = N'@pkc', @artcolumns binary(32) = NULL, @mode tinyint = 1 -- 1 = use article ordering, 2 = column ordering ) as begin declare @this_col int declare @art_col int declare @spacer nvarchar(10) declare @isset int declare @cmd nvarchar(4000) declare @modeartorder tinyint ,@modecolorder tinyint select @modeartorder = 1 ,@modecolorder = 2 ,@art_col = 0 ,@spacer = N' ' ,@cmd = N'where' if (@mode not in (@modeartorder, @modecolorder)) return 1 -- create WHERE clause DECLARE hCColid CURSOR LOCAL FAST_FORWARD FOR select colid from syscolumns where id = @src_objid order by colid asc OPEN hCColid FETCH hCColid INTO @this_col WHILE (@@fetch_status <> -1) begin if EXISTS (select name from syscolumns where id=@src_objid and @this_col=colid) begin -- If @artcolumns is not null, it is called from -- sp_scriptxupdproc or sp_scriptxdelproc -- Use counter in article column bitmap -- Otherwise use counter in pk bitmap if @artcolumns is not null begin exec @isset = dbo.sp_isarticlecolbitset @this_col, @artcolumns if @isset != 0 select @art_col = @art_col + 1 end exec @isset = dbo.sp_isarticlecolbitset @this_col, @pkcolumns if @isset != 0 begin if @artcolumns is null select @art_col = @art_col + 1 select @cmd = @cmd + @spacer + QUOTENAME(col_name( @src_objid, @this_col)) + N' = ' -- -- script the var assignment based on mode -- if (@mode = @modeartorder) select @cmd = @cmd + @prefix + convert( nvarchar, @art_col ) else select @cmd = @cmd + @prefix + convert( nvarchar, @this_col ) select @spacer = N' and ' if len( @cmd ) > 3000 begin insert into #proctext(procedure_text) values( @cmd ) select @cmd = N'' end end end FETCH hCColid INTO @this_col end CLOSE hCColid DEALLOCATE hCColid insert into #proctext(procedure_text) values( @cmd ) end go EXEC dbo.sp_MS_marksystemobject sp_scriptpkwhereclause GO grant exec on dbo.sp_scriptpkwhereclause to public go -------------------------------------------------------------------------------- --. sp_MSscript_pkvar_assignment -------------------------------------------------------------------------------- if exists (select * from sysobjects where type = 'P' and name = 'sp_MSscript_pkvar_assignment') drop procedure sp_MSscript_pkvar_assignment go raiserror('Creating procedure sp_MSscript_pkvar_assignment', 0,1) go create procedure sp_MSscript_pkvar_assignment ( @objid int, @columns binary(32), @indent int = 0, @identity_col sysname = NULL, -- Not null value used by trigger scripting @ts_col sysname = NULL, -- Not null value used by trigger scripting @primary_key_bitmap varbinary(4000) = null, -- NULL when synctran processing @fisqueuedpub bit = 0 -- 1 = processing a queued publication on publisher ) as begin -- This stored procedure will assign the '_old' var to new var -- based on @bitmap. This is to avoid using case statement -- in the where clause in the synctran pub proc, which -- will cause a table scan. declare @cmd nvarchar(4000) ,@spacer nvarchar(20) ,@indkey int ,@indid int ,@this_col int ,@col sysname ,@qualname nvarchar(512) ,@column nvarchar(255) ,@key sysname ,@src_cols int ,@total_col int ,@fcreatedcolmap bit ,@art_col int -- relative position of column declare @colmap table (relativeorder int identity(1,1), colid int) select @spacer = N'select ' ,@cmd = N'' ,@indkey = 1 ,@indid = 0 ,@fcreatedcolmap = 0 exec sp_MSget_qualified_name @objid, @qualname OUTPUT select @src_cols = max(colid) ,@total_col = count(colid) from syscolumns where id = @objid exec dbo.sp_MSpad_command @cmd output, @indent exec dbo.sp_MSflush_command @cmd output, 1, @indent if @primary_key_bitmap is null begin exec @indid = dbo.sp_MStable_has_unique_index @objid if @indid is null begin raiserror('Debug: Cannot find unique index', 16, -1) return (1) end end -- -- check if column Id match relative column order -- for trigger scripting -- if ((@total_col < @src_cols) and (@columns is null) and (@primary_key_bitmap is not null)) begin -- -- this table may have altered columns, so when we need to -- set a mapping for using the bitmaps properly as the bitmap -- always refers relative column order -- insert into @colmap (colid) select colid from syscolumns where id = @objid order by colid if (@@error != 0) begin raiserror('Could not create column mapping', 16, -1) return (1) end select @fcreatedcolmap = 1 end while (1=1) begin if @primary_key_bitmap is null begin select @key = index_col(@qualname, @indid, @indkey) if @key is null break exec dbo.sp_MSget_col_position @objid, @columns, @key, @col output, 0, NULL, @this_col output end else begin exec dbo.sp_MSget_map_position @primary_key_bitmap, @indkey, @col output, @this_col output if @this_col is null break -- -- set the actual column id for this relative order in the PK bitmap if necessary -- if (@fcreatedcolmap = 1) begin select @art_col = @this_col select @this_col = colid ,@col = 'c' + convert(sysname, colid) from @colmap where relativeorder = @art_col end else begin select @art_col = NULL end -- Get column name exec dbo.sp_MSget_colinfo @objid, @this_col, @columns, 0, @key output end select @indkey = @indkey + 1 -- -- check if identity/timestamp column were specified -- for skipping during subscriber trigger scripting -- if @key in (@identity_col, @ts_col) continue -- -- If we are scripting on publisher -- if (@primary_key_bitmap is null) begin declare @isset int -- -- skip column if not replicated -- exec @isset = dbo.sp_isarticlecolbitset @this_col, @columns if (@isset != 1) continue -- -- skip timestamp processing for queued -- if ((@fisqueuedpub = 1) and exists (select name from syscolumns where id = @objid and colid = @this_col and xtype = 189)) continue end select @cmd = @spacer + N'@c' + convert(nvarchar(10), @this_col) -- Get the new values for the columns in primary key. exec dbo.sp_MSget_synctran_column @ts_col = null, @op_type = null , -- 'ins, 'upd', 'del' @is_new = null, @primary_key_bitmap = null, @colname = null, @this_col = @this_col, @column = @column output, @from_proc = 0, @coltype = null, @type = 'pk_var', @art_col = @art_col select @cmd = @cmd + N' = ' + @column select @spacer = ', ' -- flush command if necessary exec dbo.sp_MSflush_command @cmd output, 1, @indent end end go EXEC dbo.sp_MS_marksystemobject sp_MSscript_pkvar_assignment GO grant exec on dbo.sp_MSscript_pkvar_assignment to public go -- ------------------------------------------------------------------ -- Concurrent updates -- ------------------------------------------------------------------ -------------------------------------------------------------------------------- --. sp_repldeletequeuedtran -------------------------------------------------------------------------------- if exists (select * from sysobjects where type = 'P' and name = 'sp_repldeletequeuedtran') drop procedure sp_repldeletequeuedtran go raiserror('Creating procedure sp_repldeletequeuedtran', 0,1) go create proc sp_repldeletequeuedtran ( @publisher sysname ,@publisher_db sysname ,@publication sysname ,@tranid sysname ,@orderkeylow bigint ,@orderkeyhigh bigint ) as begin declare @retcode int set nocount on -- -- Security check -- exec @retcode = dbo.sp_MSreplcheck_subscribe if @@error != 0 or @retcode != 0 return 1 -- -- validate inputs -- if (@tranid is null or @orderkeylow is null or @orderkeylow = 0 or @orderkeyhigh is null or @orderkeyhigh = 0 or @orderkeyhigh < @orderkeylow) return 1 -- -- begin local transaction -- begin transaction sp_repldeletequeuedtran save transaction sp_repldeletequeuedtran -- -- delete rows from MSreplication_queue -- delete dbo.MSreplication_queue where publisher = UPPER(@publisher) and publisher_db = @publisher_db and publication = @publication and tranid = @tranid and orderkey between @orderkeylow and @orderkeyhigh if (@@error != 0) goto Error -- -- delete row from MSrepl_queuedtraninfo -- delete dbo.MSrepl_queuedtraninfo where publisher = UPPER(@publisher) and publisher_db = @publisher_db and publication = @publication and tranid = @tranid if (@@error != 0) goto Error -- -- commit local transaction -- commit transaction sp_repldeletequeuedtran -- -- all done -- return 0 Error: rollback transaction sp_repldeletequeuedtran commit transaction return 1 end go exec dbo.sp_MS_marksystemobject sp_repldeletequeuedtran go grant execute on dbo.sp_repldeletequeuedtran to public go -------------------------------------------------------------------------------- --. sp_replsqlqgetrows -------------------------------------------------------------------------------- if exists (select * from sysobjects where type = 'P' and name = 'sp_replsqlqgetrows') drop procedure sp_replsqlqgetrows go raiserror('Creating procedure sp_replsqlqgetrows', 0,1) go create proc sp_replsqlqgetrows ( @publisher sysname ,@publisherdb sysname ,@publication sysname ,@batchsize int = 1000 ) as begin declare @retcode int set nocount on -- -- Security check -- exec @retcode = dbo.sp_MSreplcheck_subscribe if @@error != 0 or @retcode != 0 return 1 -- -- does the queue table exist -- if exists (select * from dbo.sysobjects where name = 'MSreplication_queue') begin declare @totcommandcount bigint ,@trancount bigint -- -- does the tran info table exist -- if not exists (select * from dbo.sysobjects where name = 'MSrepl_queuedtraninfo') begin -- -- tran info table does not exist - create and populate it -- exec @retcode = sp_MScreate_sub_tables @tran_sub_table = 0, @property_table = 0, @sqlqueue_table = 1 if (@@error != 0 or @retcode != 0) return 1 end -- -- At this point both queue table and tran info table exist -- check the command count -- select @totcommandcount = sum(commandcount) ,@trancount = count(tranid) from dbo.MSrepl_queuedtraninfo with (READPAST) where publisher = UPPER(@publisher) and publisher_db = @publisherdb and publication = @publication if (@trancount > 1 and @totcommandcount > @batchsize) begin -- -- prepare a list of transactions to read -- declare @tranid sysname ,@batchcount bigint ,@curtrancommandcount bigint declare @trantab table (tranid sysname primary key) select @batchcount = 0 declare #htcdataseq cursor local for select tranid, commandcount from dbo.MSrepl_queuedtraninfo with (READPAST) where publisher = UPPER(@publisher) and publisher_db = @publisherdb and publication = @publication order by maxorderkey asc open #htcdataseq fetch #htcdataseq into @tranid, @curtrancommandcount if (@@error != 0) return 1 while (@@fetch_status != -1) begin -- -- Are we done -- if (@batchcount > @batchsize) begin -- -- we are done selecting the transactions to process -- break end else begin -- -- include this transaction -- update the batch counter -- insert into @trantab (tranid) values (@tranid) if (@@error != 0) return 1 select @batchcount = @batchcount + @curtrancommandcount end -- -- fetch next transaction to process -- fetch #htcdataseq into @tranid, @curtrancommandcount end close #htcdataseq deallocate #htcdataseq if (@@error != 0) return 1 -- -- do the join for the select transactions -- select the transactions in the order they were committed (maxorderkey ascending). -- for each transaction - the commands are ordered using orderkey (ascending) -- select q.tranid, q.datalen, q.data, q.commandtype, q.insertdate, q.orderkey, q.cmdstate from (dbo.MSreplication_queue as q with (READPAST) join (dbo.MSrepl_queuedtraninfo as t with (READPAST) join @trantab as tt on t.tranid = tt.tranid collate database_default) on q.publisher = t.publisher and q.publisher_db = t.publisher_db and q.publication = t.publication and q.tranid = t.tranid) where t.publisher = UPPER(@publisher) and t.publisher_db = @publisherdb and t.publication = @publication order by t.maxorderkey asc, q.orderkey asc end else begin -- -- do the join for all the transactions -- select the transactions in the order they were committed (maxorderkey ascending). -- for each transaction - the commands are ordered using orderkey (ascending) -- select q.tranid, q.datalen, q.data, q.commandtype, q.insertdate, q.orderkey, q.cmdstate from (dbo.MSreplication_queue as q with (READPAST) join dbo.MSrepl_queuedtraninfo as t with (READPAST) on q.publisher = t.publisher and q.publisher_db = t.publisher_db and q.publication = t.publication and q.tranid = t.tranid) where t.publisher = UPPER(@publisher) and t.publisher_db = @publisherdb and t.publication = @publication order by t.maxorderkey asc, q.orderkey asc end end else begin -- -- Queue table does not exist -- create empty rowset -- declare @nomesgs TABLE (tranid sysname, datalen int, data varbinary(8000), commandtype int, insertdate datetime, orderkey bigint, cmdstate bit) select * from @nomesgs end -- -- check error -- if (@@error != 0) return 1 -- -- All done -- return 0 end go exec dbo.sp_MS_marksystemobject sp_replsqlqgetrows go grant execute on dbo.sp_replsqlqgetrows to public go -------------------------------------------------------------------------------- --. sp_subscription_cleanup -------------------------------------------------------------------------------- if exists (select * from sysobjects where type = 'P' and name = 'sp_subscription_cleanup') drop procedure sp_subscription_cleanup go raiserror('Creating procedure sp_subscription_cleanup', 0,1) GO CREATE PROCEDURE sp_subscription_cleanup ( @publisher sysname, @publisher_db sysname, @publication sysname = NULL, @reserved nvarchar(10) = NULL ) AS BEGIN declare @object_name sysname ,@object_type char(2) ,@independent_agent bit ,@retcode int ,@synctran_bit int ,@parent_obj int ,@object_id int ,@cmd nvarchar(4000) select @synctran_bit = 256 /* ** Security Check */ EXEC @retcode = dbo.sp_MSreplcheck_subscribe IF @@ERROR <> 0 or @retcode <> 0 RETURN(1) if @publication = '' OR @publication is NULL select @independent_agent = 0 else select @independent_agent = 1 IF exists (select name from sysobjects where name = 'MSreplication_objects') BEGIN declare object_cursor CURSOR LOCAL FAST_FORWARD for select DISTINCT object_name, object_type from MSreplication_objects o where (UPPER(o.publisher) = UPPER(@publisher) and o.publisher_db = @publisher_db and o.publication = @publication) or @reserved = 'drop_all' OPEN object_cursor FETCH object_cursor INTO @object_name, @object_type WHILE (@@fetch_status <> -1) BEGIN IF @object_type = 'T' begin select @parent_obj = NULL select @parent_obj = parent_obj, @object_id = id from sysobjects where name = @object_name if @parent_obj is not null begin -- Unmark synctran bit update sysobjects set replinfo = replinfo & ~@synctran_bit where id = @parent_obj and (replinfo & @synctran_bit) <> 0 IF @@ERROR <> 0 GOTO UNDO exec @retcode = dbo.sp_MSdrop_object @object_id = @object_id if @retcode <> 0 or @@error <> 0 goto UNDO -- Clean up identity range entry -- Since we only support one trigger per subscriber table -- we assume identity range row can not be reused by multiple -- subscriptions. IF EXISTS(select * from sysobjects where type='U' and name = 'MSsub_identity_range') BEGIN if exists (select * from MSsub_identity_range where objid = @parent_obj) begin -- Drop the identity range constraits. exec @retcode = dbo.sp_MSreseed @objid = @parent_obj, -- range or seed can be anything @next_seed = 10, @range = 10, @is_publisher = -1, @check_only = 1, @drop_only = 1 IF @retcode <> 0 or @@ERROR <> 0 GOTO UNDO delete MSsub_identity_range where objid = @parent_obj IF @@ERROR <> 0 GOTO UNDO end IF NOT EXISTS (SELECT * FROM MSsub_identity_range) BEGIN DROP TABLE MSsub_identity_range IF @@ERROR <> 0 GOTO UNDO END END end end delete from MSreplication_objects where object_name=@object_name FETCH object_cursor INTO @object_name, @object_type END CLOSE object_cursor DEALLOCATE object_cursor if not exists (select * from MSreplication_objects) begin drop table MSreplication_objects IF @@ERROR <> 0 GOTO UNDO end END -- -- cleanup queued conflict tables -- IF exists (select name from dbo.sysobjects where name = 'MSsubscription_agents') BEGIN declare @agent_id int ,@cft_table sysname ,@owner sysname -- -- first get the agent(s) for this queued subscription(s) and -- declare #agent_cursor CURSOR LOCAL FAST_FORWARD for select id from dbo.MSsubscription_agents where ((UPPER(publisher) = UPPER(@publisher) and publisher_db = @publisher_db and publication = @publication) or (@reserved = 'drop_all')) and update_mode in (2,3,4,5) open #agent_cursor fetch #agent_cursor into @agent_id while (@@fetch_status != -1) begin -- -- drop the conflict table for each article in this subscription -- if exists (select name from dbo.sysobjects where name = 'MSsubscription_articles') begin declare #object_cursor CURSOR LOCAL FAST_FORWARD for select owner, cft_table from dbo.MSsubscription_articles where agent_id = @agent_id OPEN #object_cursor FETCH #object_cursor INTO @owner, @cft_table WHILE (@@fetch_status != -1) BEGIN -- -- drop the conflict table(s) for this article - ignore errors -- select @cmd = case when (@owner IS NULL) then N'if exists (select * from dbo.sysobjects where name = N''' + master.dbo.fn_MSgensqescstr(@cft_table) collate database_default + ''') drop table ' + quotename(master.dbo.fn_MSgensqescstr(@cft_table)) collate database_default else N'if exists (select * from dbo.sysobjects where name = N''' + master.dbo.fn_MSgensqescstr(@cft_table) collate database_default + ''') drop table ' + quotename(master.dbo.fn_MSgensqescstr(@owner)) collate database_default + N'.' + quotename(master.dbo.fn_MSgensqescstr(@cft_table)) collate database_default end execute ( @cmd ) -- get next row FETCH #object_cursor INTO @owner, @cft_table END CLOSE #object_cursor DEALLOCATE #object_cursor -- -- delete entries from MSsubscription_articles for this agent id -- delete dbo.MSsubscription_articles where agent_id = @agent_id -- -- drop MSsubscription_articles if empty (should we do it) -- IF NOT EXISTS (SELECT * FROM dbo.MSsubscription_articles) BEGIN DROP TABLE dbo.MSsubscription_articles IF @@ERROR != 0 GOTO UNDO END end -- -- get the next agent -- fetch #agent_cursor into @agent_id end close #agent_cursor deallocate #agent_cursor END -- -- clean discarded queued transactions -- if exists (select name from dbo.sysobjects where name = 'MSreplication_queue') begin delete dbo.MSreplication_queue where (publisher = UPPER(@publisher) and publisher_db = @publisher_db and publication = @publication) or @reserved = 'drop_all' end if exists (select name from dbo.sysobjects where name = 'MSrepl_queuedtraninfo') begin delete dbo.MSrepl_queuedtraninfo where (publisher = UPPER(@publisher) and publisher_db = @publisher_db and publication = @publication) or @reserved = 'drop_all' end IF exists (select name from sysobjects where name = 'MSreplication_subscriptions') BEGIN delete from MSreplication_subscriptions where (UPPER(publisher) = UPPER(@publisher) AND publisher_db = @publisher_db AND -- Drop the subscription as long as the publication name matches even if -- the publication is not independent agent -- This behaviour is expected by sp_droppullsubscription -- -- independent_agent = @independent_agent and ((@independent_agent=0 and independent_agent = 0) or publication = @publication)) or @reserved = 'drop_all' IF NOT EXISTS (SELECT * FROM MSreplication_subscriptions) BEGIN DROP TABLE MSreplication_subscriptions IF @@ERROR <> 0 GOTO UNDO END END IF exists (select name from sysobjects where name = 'MSsubscription_agents') BEGIN delete from MSsubscription_agents where (UPPER(publisher) = UPPER(@publisher) AND publisher_db = @publisher_db AND -- Drop the subscription as long as the publication name matches even if -- the publication is not independent agent -- This behaviour is expected by sp_droppullsubscription (publication = @publication or (@independent_agent=0 and publication = N'ALL'))) or @reserved = 'drop_all' -- Delete the agent entry if no corresponding rows found in MSreplication_subscription -- table. -- This is to cleanup share agent entry. -- This behaviour is expected by sp_droppullsubscription if object_id('MSreplication_subscriptions') is not null begin if not exists (select * from MSreplication_subscriptions s where s.publisher = @publisher and s.publisher_db = @publisher_db and s.independent_agent = 0) delete from MSsubscription_agents where publisher = @publisher and publisher_db = @publisher_db and publication = N'ALL' end else delete MSsubscription_agents IF NOT EXISTS (SELECT * FROM MSsubscription_agents) BEGIN DROP TABLE MSsubscription_agents IF @@ERROR <> 0 GOTO UNDO END END IF EXISTS(select * from sysobjects where type=N'U' and name = 'MSsubscription_properties') BEGIN DELETE FROM MSsubscription_properties WHERE (UPPER(publisher) = UPPER(@publisher) AND publisher_db = @publisher_db AND publication = @publication) or @reserved = 'drop_all' IF @@ERROR <> 0 GOTO UNDO IF NOT EXISTS (SELECT * FROM MSsubscription_properties) BEGIN exec @retcode = dbo.sp_MSsub_cleanup_prop_table IF @@ERROR <> 0 or @retcode <> 0 GOTO UNDO END END -- Ignore errors. exec dbo.sp_MSsub_cleanup_orphans return (0) UNDO: return(1) END GO exec dbo.sp_MS_marksystemobject sp_subscription_cleanup go -------------------------------------------------------------------------------- --. sp_MSreset_queue -------------------------------------------------------------------------------- if exists (select * from sysobjects where type = 'P' and name = 'sp_MSreset_queue') drop procedure sp_MSreset_queue go raiserror('Creating procedure sp_MSreset_queue', 0,1) GO CREATE PROCEDURE sp_MSreset_queue ( @publisher sysname, -- publishing server name @publisher_db sysname, -- publishing database name. @publication sysname, -- publication name, @artid int) as begin declare @subserver sysname ,@subdbname sysname ,@queue_id sysname ,@update_mode int ,@retcode smallint ,@vbartid varbinary(20) ,@queue_server sysname set nocount on -- -- Security Check -- EXEC @retcode = dbo.sp_MSreplcheck_subscribe IF @@ERROR <> 0 or @retcode <> 0 RETURN(1) select @subserver = @@servername, @subdbname = db_name(), @update_mode = update_mode, @queue_id = queue_id, @queue_server = queue_server from MSsubscription_agents where UPPER(publisher) = UPPER(@publisher) and publisher_db = @publisher_db and publication = @publication if (@update_mode in (2,3)) begin -- -- MSMQ processing -- prefix the queue_id with queue server in direct format -- and then perform the queue reset -- select @queue_id = N'DIRECT=OS:' + @queue_server + N'\PRIVATE$\' + @queue_id begin distributed tran exec @retcode = master.dbo.xp_resetqueue @queue_id, @subserver, @subdbname, @publication, @artid if (@retcode != 0 or @@error != 0) begin if (@@trancount > 0) rollback tran return (1) end end else if (@update_mode in (4,5)) begin declare @tranid sysname begin tran -- -- process MSreplication_queue -- select @retcode = 0 if (exists (select * from sysobjects where name = 'MSreplication_queue')) begin -- -- Strictly speaking we do no need -- to delete but, makes it easy for -- the queue reader agent -- Do not delete any reset messages -- delete dbo.MSreplication_queue where publisher = UPPER(@publisher) and publisher_db = @publisher_db and publication = @publication and tranid not like N'sub-reset%' end else begin -- -- first queue subscription is being initialized -- create queue if necessary -- exec @retcode = sp_MScreate_sub_tables @tran_sub_table = 0, @property_table = 0, @sqlqueue_table = 1 end if (@retcode != 0 or @@error != 0) begin if (@@trancount > 0) rollback tran return (1) end -- -- process MSrepl_queuedtraninfo -- if (exists (select * from sysobjects where name = 'MSrepl_queuedtraninfo')) begin -- -- Strictly speaking we do no need -- to delete but, makes it easy for -- the queue reader agent -- Do not delete any reset messages -- delete dbo.MSrepl_queuedtraninfo where publisher = UPPER(@publisher) and publisher_db = @publisher_db and publication = @publication and tranid not like N'sub-reset%' end else begin -- -- first queue subscription is being initialized -- create the traninfo if necessary -- exec @retcode = sp_MScreate_sub_tables @tran_sub_table = 0, @property_table = 0, @sqlqueue_table = 1 end if (@retcode != 0 or @@error != 0) begin if (@@trancount > 0) rollback tran return (1) end -- -- for subscription reinitialization we -- need to insert a RESYNC command message -- select @vbartid = cast(@artid as varbinary(20)) ,@tranid = N'sub-reset-' + cast(NEWID() as sysname) insert into dbo.MSreplication_queue (publisher, publisher_db, publication,tranid, commandtype, data, datalen) values (UPPER(@publisher), @publisher_db, @publication, @tranid, 2, @vbartid, datalength(@vbartid)) if ((@@error != 0) or (@retcode != 0)) begin if (@@trancount > 0) rollback tran return (1) end -- -- add an entry in MSrepl_queuedtraninfo -- insert into dbo.MSrepl_queuedtraninfo (publisher,publisher_db,publication,tranid,maxorderkey,commandcount) values (UPPER(@publisher),@publisher_db,@publication,@tranid,@@identity,1) end commit tran return 0 end GO exec dbo.sp_MS_marksystemobject sp_MSreset_queue go grant execute on dbo.sp_MSreset_queue to public go -------------------------------------------------------------------------------- --. sp_MScreate_sub_tables -------------------------------------------------------------------------------- if exists (select * from sysobjects where type = 'P' and name = 'sp_MScreate_sub_tables') drop procedure sp_MScreate_sub_tables go raiserror('Creating procedure sp_MScreate_sub_tables', 0,1) GO CREATE PROCEDURE sp_MScreate_sub_tables ( @tran_sub_table bit = 0, @property_table bit = 1, @sqlqueue_table bit = 0 ) as BEGIN set nocount on declare @retcode int IF @tran_sub_table = 1 and (NOT EXISTS (SELECT * FROM sysobjects WHERE type = 'U' AND name = 'MSreplication_subscriptions')) BEGIN CREATE TABLE dbo.MSreplication_subscriptions ( publisher sysname NOT NULL, publisher_db sysname NOT NULL, publication sysname NULL, independent_agent bit NOT NULL, subscription_type int NOT NULL, distribution_agent sysname NULL, time smalldatetime NOT NULL, description nvarchar(255) NULL, transaction_timestamp varbinary(16) NOT NULL, -- SyncTran update_mode tinyint NOT NULL, agent_id binary(16) NULL, subscription_guid binary(16) NULL, subid binary(16) NULL, immediate_sync bit NOT NULL default 1 -- sync_mode with a default of 1 ) IF @@ERROR <> 0 GOTO UNDO CREATE UNIQUE CLUSTERED INDEX uc1MSReplication_subscriptions ON MSreplication_subscriptions(publication, publisher_db, publisher, subscription_type) IF @@ERROR <> 0 GOTO UNDO EXEC dbo.sp_MS_marksystemobject 'MSreplication_subscriptions' IF @@ERROR <> 0 GOTO UNDO END IF @tran_sub_table = 1 BEGIN IF NOT EXISTS (SELECT * FROM sysobjects WHERE type = 'U' AND name = 'MSsubscription_agents') BEGIN CREATE TABLE dbo.MSsubscription_agents ( id int identity, publisher sysname NOT NULL, publisher_db sysname NOT NULL, publication sysname NOT NULL, subscription_type int NOT NULL, queue_id sysname NULL, update_mode tinyint default 0 not null, -- 0 = read only, 1 = sync/immediate, 2 = queued, 3 = failover, 4 = sqlqueued, 5 = sqlqueued failover failover_mode bit default 0 not null, -- 0 - sync/immediate, 1 = queued spid int NOT NULL, login_time datetime NOT NULL, allow_subscription_copy bit default 0 not null, attach_state int default 0 not null, -- 0: not attached 1 attached but not processed 2 attached and processed. attach_version binary(16) default newid() not null, last_sync_status int NULL, -- allow null for upgrade last_sync_summary sysname NULL, -- allow null for upgrade last_sync_time datetime NULL, -- allow null for upgrade queue_server sysname NULL -- only used for MSMQ based updating subscribers ) IF @@ERROR <> 0 GOTO UNDO CREATE unique CLUSTERED INDEX ucMSsubscription_agents ON dbo.MSsubscription_agents (publication, publisher_db, publisher, subscription_type) CREATE INDEX ucMSsubscription_agents_id ON dbo.MSsubscription_agents (id) EXEC dbo.sp_MS_marksystemobject 'MSsubscription_agents' IF @@ERROR <> 0 GOTO UNDO grant select on dbo.MSsubscription_agents to public IF @@ERROR <> 0 GOTO UNDO END ELSE BEGIN -- -- table exists - add new columns -- if not exists (select * from dbo.syscolumns where id = object_id('MSsubscription_agents') and name = 'queue_server') begin alter table dbo.MSsubscription_agents add queue_server sysname NULL exec dbo.sp_MSupdate_mqserver_subdb end END END IF @property_table = 1 and NOT EXISTS (SELECT * FROM sysobjects WHERE type = 'U' AND name = 'MSsubscription_properties') BEGIN BEGIN TRAN CREATE TABLE dbo.MSsubscription_properties ( publisher sysname NOT NULL, publisher_db sysname NOT NULL, publication sysname NOT NULL, publication_type int NOT NULL, publisher_login sysname NULL, publisher_password nvarchar(524) NULL, publisher_security_mode int NOT NULL, distributor sysname NULL, distributor_login sysname NULL, distributor_password nvarchar(524) NULL, distributor_security_mode int NOT NULL, ftp_address sysname NULL, ftp_port int NULL, ftp_login sysname NULL, ftp_password nvarchar(524) NULL, alt_snapshot_folder nvarchar(255) NULL, working_directory nvarchar(255) NULL, use_ftp bit default 0 NOT NULL, dts_package_name sysname NULL, dts_package_password nvarchar(524) NULL, -- default to be at the subscriber dts_package_location int default 1 NOT NULL, enabled_for_syncmgr bit default 0 NOT NULL, offload_agent bit default 0 NOT NULL, offload_server sysname NULL, dynamic_snapshot_location nvarchar(255) NULL ) IF @@ERROR <> 0 GOTO UNDO CREATE UNIQUE CLUSTERED INDEX uc1MSsubscription_properties ON MSsubscription_properties(publication, publisher_db, publisher) IF @@ERROR <> 0 GOTO UNDO EXEC @retcode = dbo.sp_MS_marksystemobject 'MSsubscription_properties' if @retcode <> 0 or @@error <> 0 GOTO UNDO COMMIT TRAN END IF @sqlqueue_table = 1 BEGIN declare @folddata bit BEGIN TRAN if EXISTS (SELECT * FROM dbo.sysobjects WHERE type = 'U' AND name = 'MSreplication_queue') BEGIN -- -- table exists - check if we need to add columns -- if not exists (select * from dbo.syscolumns where id = object_id('MSreplication_queue') and name = 'cmdstate') BEGIN ALTER TABLE dbo.MSreplication_queue ADD cmdstate bit DEFAULT 0 NOT NULL IF @@ERROR <> 0 GOTO UNDO END -- -- change data column from text to varbinary(8000) -- SPECIAL CASE : since a simple ALTER does not work here -- we create a temp table to save the existing data and then -- recreate the table -- if exists (select * from dbo.syscolumns where id = object_id('MSreplication_queue') and name = 'data' and xtype = 34) begin -- -- save existing column data -- if exists (select * from dbo.MSreplication_queue) begin select @folddata = 1 create table #olddata ( publisher sysname collate database_default not null , publisher_db sysname collate database_default not null , publication sysname collate database_default not null , tranid sysname collate database_default not null , data varbinary(8000) NULL , datalen int, commandtype int, insertdate datetime , orderkey bigint, cmdstate bit) insert into #olddata select publisher, publisher_db, publication, tranid, CAST(data as varbinary(8000)), datalen, commandtype, insertdate, orderkey, cmdstate from dbo.MSreplication_queue if @@error != 0 goto UNDO end -- -- drop table -- DROP TABLE dbo.MSreplication_queue IF @@ERROR <> 0 GOTO UNDO end END -- -- Create table if it does not exist -- if NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE type = 'U' AND name = 'MSreplication_queue') BEGIN CREATE TABLE dbo.MSreplication_queue ( publisher sysname NOT NULL , publisher_db sysname NOT NULL , publication sysname NOT NULL , tranid sysname NOT NULL , data varbinary(8000) NULL , datalen int DEFAULT 0 , commandtype int NULL , insertdate datetime DEFAULT GETDATE(), orderkey bigint IDENTITY(1,1) PRIMARY KEY, cmdstate bit DEFAULT 0 NOT NULL ) IF @@ERROR <> 0 GOTO UNDO /**** CREATE NONCLUSTERED INDEX nc1MSreplication_queue ON MSreplication_queue(tranid) IF @@ERROR <> 0 GOTO UNDO ****/ EXEC @retcode = dbo.sp_MS_marksystemobject 'MSreplication_queue' if @retcode <> 0 or @@error <> 0 GOTO UNDO -- -- Do we need to restore old data -- if (@folddata = 1) begin insert dbo.MSreplication_queue (publisher, publisher_db, publication, tranid, data, datalen, commandtype, insertdate, cmdstate) select publisher, publisher_db, publication, tranid, data, datalen, commandtype, insertdate, cmdstate from #olddata order by orderkey if @@error != 0 goto UNDO drop table #olddata if @@error != 0 goto UNDO end END -- -- Table MSrepl_queuedtraninfo -- Create table if it does not exist -- if NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE type = 'U' AND name = 'MSrepl_queuedtraninfo') BEGIN CREATE TABLE dbo.MSrepl_queuedtraninfo ( publisher sysname NOT NULL , publisher_db sysname NOT NULL , publication sysname NOT NULL , tranid sysname NOT NULL , maxorderkey bigint NOT NULL, commandcount bigint NOT NULL ) IF @@ERROR <> 0 GOTO UNDO CREATE CLUSTERED INDEX c1MSrepl_queuedtraninfo ON MSrepl_queuedtraninfo(publisher,publisher_db,publication,tranid) IF @@ERROR <> 0 GOTO UNDO EXEC @retcode = dbo.sp_MS_marksystemobject 'MSrepl_queuedtraninfo' if @retcode <> 0 or @@error <> 0 GOTO UNDO -- -- Populate this table as necessary -- exec @retcode = dbo.sp_populateqtraninfo if @retcode <> 0 or @@error <> 0 GOTO UNDO END COMMIT TRAN END return(0) UNDO: IF @@TRANCOUNT = 1 ROLLBACK TRAN ELSE COMMIT TRAN return(1) END go exec dbo.sp_MS_marksystemobject sp_MScreate_sub_tables go grant exec on dbo.sp_MScreate_sub_tables to public go -------------------------------------------------------------------------------- --. sp_MSsendtosqlqueue -------------------------------------------------------------------------------- if exists (select * from sysobjects where type = 'P' and name = 'sp_MSsendtosqlqueue') drop procedure sp_MSsendtosqlqueue go raiserror(15339,-1,-1,'sp_MSsendtosqlqueue') GO create proc sp_MSsendtosqlqueue ( @objid int ,@publisher sysname ,@publisher_db sysname ,@publication sysname ,@owner sysname ,@tranid sysname ,@data varbinary(8000) ,@datalen int ,@commandtype int = 1 ,@cmdstate bit = 0 ) as begin set nocount on -- -- Security check -- Make sure this proc is infact being called from the given object(trigger) -- if (trigger_nestlevel(@objid) = 0) begin raiserror(14126, 16, 2) return 1 end -- -- Security check: the caller of this SP has to be one of the -- predefined replication objects for this subscription -- if (@objid not in (select object_id(quotename(@owner) + N'.' + quotename([object_name])) from dbo.MSreplication_objects where publisher = @publisher and publisher_db = @publisher_db and publication = @publication and object_type = 'T')) begin raiserror(14126, 16, 3) return 1 end -- -- insert into the queue table -- insert into dbo.MSreplication_queue (publisher,publisher_db,publication,tranid,data,datalen,commandtype,cmdstate) values(UPPER(@publisher),@publisher_db,@publication,@tranid,@data,@datalen,@commandtype,@cmdstate) if (@@error != 0) return 1 -- -- Update MSrepl_queuedtraninfo -- if exists (select * from dbo.MSrepl_queuedtraninfo where publisher = UPPER(@publisher) and publisher_db = @publisher_db and publication = @publication and tranid = @tranid) begin -- -- row for this transaction exists - update it -- update dbo.MSrepl_queuedtraninfo set maxorderkey = @@identity ,commandcount = commandcount + 1 where publisher = UPPER(@publisher) and publisher_db = @publisher_db and publication = @publication and tranid = @tranid end else begin -- -- row for this transaction does not exist - insert it -- insert into dbo.MSrepl_queuedtraninfo (publisher,publisher_db,publication,tranid,maxorderkey,commandcount) values (UPPER(@publisher),@publisher_db,@publication,@tranid,@@identity,1) end if (@@error != 0) return 1 -- -- all done -- return 0 end go exec dbo.sp_MS_marksystemobject sp_MSsendtosqlqueue go grant execute on dbo.sp_MSsendtosqlqueue to public go -------------------------------------------------------------------------------- --. sp_populateqtraninfo -------------------------------------------------------------------------------- if exists (select * from sysobjects where type = 'P' and name = 'sp_populateqtraninfo') drop procedure sp_populateqtraninfo go raiserror('Creating procedure sp_populateqtraninfo', 0,1) GO CREATE PROCEDURE sp_populateqtraninfo as begin -- -- initiate local transaction -- begin tran sp_populateqtraninfo save tran sp_populateqtraninfo -- -- see if we need to populate -- if EXISTS (SELECT * FROM dbo.MSreplication_queue) begin -- -- There is data in queue -- Populate MSrepl_queuedtraninfo based on what is there -- declare @publisher sysname ,@publisherdb sysname ,@publication sysname ,@tranid sysname ,@orderkey bigint declare #htctranseq cursor local for select publisher, publisher_db, publication, tranid, orderkey from dbo.MSreplication_queue with (READPAST) order by orderkey asc open #htctranseq fetch #htctranseq into @publisher, @publisherdb, @publication, @tranid, @orderkey IF @@ERROR <> 0 goto cleanup while (@@fetch_status != -1) begin -- -- update/insert entry in MSrepl_queuedtraninfo for this transaction -- if exists ( select * from dbo.MSrepl_queuedtraninfo where publisher = UPPER(@publisher) and publisher_db = @publisherdb and publication = @publication and tranid = @tranid) begin -- -- row for this transaction exists - update it -- update dbo.MSrepl_queuedtraninfo set maxorderkey = @orderkey ,commandcount = commandcount + 1 where publisher = UPPER(@publisher) and publisher_db = @publisherdb and publication = @publication and tranid = @tranid end else begin -- -- row for this transaction does not exist - insert it -- insert into dbo.MSrepl_queuedtraninfo (publisher,publisher_db,publication,tranid,maxorderkey,commandcount) values (UPPER(@publisher),@publisherdb,@publication,@tranid,@orderkey,1) end IF @@ERROR <> 0 goto cleanup -- -- get the next row -- fetch #htctranseq into @publisher, @publisherdb, @publication, @tranid, @orderkey end close #htctranseq deallocate #htctranseq end -- -- commit local transaction -- commit tran sp_populateqtraninfo -- -- all done -- return 0 cleanup: -- -- error cleanup -- rollback local tran -- rollback tran sp_populateqtraninfo commit tran return 1 end go exec dbo.sp_MS_marksystemobject sp_populateqtraninfo go -------------------------------------------------------------------------------- --. sp_vupgrade_subscription_tables -------------------------------------------------------------------------------- if exists (select * from sysobjects where type = 'P' and name = 'sp_vupgrade_subscription_tables') drop procedure sp_vupgrade_subscription_tables go raiserror('Creating procedure sp_vupgrade_subscription_tables', 0,1) GO create procedure sp_vupgrade_subscription_tables as begin /* * Process schema and metadata changes common to transactional pub/sub databases. Exception is creation * of MSrepl_identity_range table needed for tran and merge publishing databases and merge subscr db's. * * Setup version upgrade procedure call order: * sp_vupgrade_replication -> sp_vupgrade_subscription_databases -> sp_vupgrade_subscription_tables */ -- raiserror('sp_vupgrade_subscription_tables', 0,1) with nowait set nocount on DECLARE @table_name sysname declare @retcode int -- Identity range management; create in publishing databases if exists (select * from sysobjects where name = 'sysarticles') begin if not exists (select * from sysobjects where name = 'MSpub_identity_range') begin CREATE TABLE dbo.MSpub_identity_range ( objid int not null, range bigint not null, pub_range bigint not null, current_pub_range bigint not null, threshold int not null, last_seed bigint null -- It will be not when uninitialized. ) IF @@ERROR <> 0 BEGIN return(1) END -- mark the index as a system object exec dbo.sp_MS_marksystemobject 'MSpub_identity_range' IF @@ERROR <> 0 BEGIN return(1) END create unique nonclustered index unc1MSpub_identity_range on MSpub_identity_range (objid) IF @@ERROR <> 0 BEGIN return(1) END end end -- Identity range management; create in all publishing databases and merge subscription databases (Shiloh) if exists (select * from sysobjects where name = 'sysmergearticles') begin if not exists (select * from sysobjects where name = 'MSrepl_identity_range') begin create table dbo.MSrepl_identity_range ( objid int not NULL primary key, next_seed bigint NULL, --resource control pub_range bigint NULL, --publisher range range bigint NULL, -- set by sp_addmergearticle max_identity bigint NULL, --resource control threshold int NULL, --in percentage, set by sp_addmergearticle current_max bigint NULL --max value for current check constraint,set by sp_addmergearticle ) exec dbo.sp_MS_marksystemobject MSrepl_identity_range if @@ERROR <> 0 return(1) grant select on MSrepl_identity_range to public end end -- Create unique index on tables that did not have one previously. The index is not created -- if duplicates rows exist in the table. -- MSreplication_subscriptions (SQL7.0 SP1) SELECT @table_name = N'MSreplication_subscriptions' IF EXISTS ( SELECT * FROM sysobjects WHERE name = 'MSreplication_subscriptions' ) or EXISTS ( SELECT * FROM sysobjects WHERE name = 'MSsubscription_agents' ) BEGIN IF EXISTS ( SELECT publication, publisher_db, publisher, subscription_type FROM MSreplication_subscriptions GROUP BY publication, publisher_db, publisher, subscription_type HAVING COUNT(*) > 1 ) RAISERROR (21203, 10, 1, @table_name) ELSE IF NOT EXISTS ( SELECT * FROM sysindexes WHERE name = 'uc1MSReplication_subscriptions' AND id = OBJECT_ID('MSreplication_subscriptions') ) CREATE UNIQUE CLUSTERED INDEX uc1MSReplication_subscriptions ON MSreplication_subscriptions(publication, publisher_db, publisher, subscription_type) -- Create MSsubscription_agents table exec @retcode = dbo.sp_MScreate_sub_tables @tran_sub_table = 1, @property_table = 0, @sqlqueue_table = 0 IF @@ERROR <> 0 or @retcode <> 0 RETURN(1) -- Add new columns to MSsubscription_agents, for 8.0 pre beta upgrade. -- status of the last sync (Shiloh) if not exists (select * from syscolumns where id = object_id('MSsubscription_agents') and name = 'last_sync_status') begin alter table MSsubscription_agents add last_sync_status int NULL if @@ERROR <> 0 return 1 end -- summary message of the last sync (Shiloh) if not exists (select * from syscolumns where id = object_id('MSsubscription_agents') and name = 'last_sync_summary') begin alter table MSsubscription_agents add last_sync_summary sysname NULL if @@ERROR <> 0 return 1 end -- summary message of the last sync (Shiloh) if not exists (select * from syscolumns where id = object_id('MSsubscription_agents') and name = 'last_sync_time') begin alter table MSsubscription_agents add last_sync_time datetime NULL if @@ERROR <> 0 return 1 end END -- MSreplication_objects (SQL7.0 SP1) SELECT @table_name = N'MSreplication_objects' IF EXISTS ( SELECT * FROM sysobjects WHERE name = 'MSreplication_objects' ) BEGIN IF EXISTS ( SELECT object_name FROM MSreplication_objects GROUP BY object_name HAVING COUNT(*) > 1 ) RAISERROR (21203, 10, 3, @table_name) ELSE IF NOT EXISTS ( SELECT * FROM sysindexes WHERE name = 'ucMSreplication_objects' AND id = OBJECT_ID('MSreplication_objects') ) CREATE UNIQUE CLUSTERED INDEX ucMSreplication_objects ON dbo.MSreplication_objects(object_name) END -- MSreplication_queue (SQL2000 SP3) SELECT @table_name = N'MSreplication_queue' IF EXISTS ( SELECT * FROM sysobjects WHERE name = @table_name ) BEGIN exec @retcode = sp_MScreate_sub_tables @tran_sub_table = 0, @property_table = 0, @sqlqueue_table = 1 IF @@ERROR <> 0 or @retcode <> 0 RETURN(1) END -- MSrepl_queuedtraninfo (SQL2000 SP3) SELECT @table_name = N'MSrepl_queuedtraninfo' IF EXISTS ( SELECT * FROM sysobjects WHERE name = @table_name ) BEGIN exec @retcode = sp_MScreate_sub_tables @tran_sub_table = 0, @property_table = 0, @sqlqueue_table = 1 IF @@ERROR <> 0 or @retcode <> 0 RETURN(1) END end go exec dbo.sp_MS_marksystemobject sp_vupgrade_subscription_tables go dump tran master with no_log go sp_configure 'allow updates',0 go reconfigure with override go checkpoint go -- -- Now we will upgrade the subscriber databases to ensure -- that the new tables needed for queued replication are in place -- set nocount on declare @dbname nvarchar(270), @has_dbaccess bit declare current_db CURSOR LOCAL FAST_FORWARD for select N'[' + replace(name, N']', N']]') + N']', has_dbaccess(name) from master..sysdatabases WHERE name <> N'master' collate database_default AND name <> N'tempdb' collate database_default AND name <> N'msdb' collate database_default for read only -- Note: dbname is quoted! open current_db fetch current_db into @dbname, @has_dbaccess while ( @@fetch_status <> -1 ) begin -- upgrade repl tables in sub dbs if needed - sub dbs are not marked with subscribed status -- skip any database in an offline state and write warning to upgrade log if ( @has_dbaccess = 1 ) begin raiserror( 21377, 0, 1, @dbname) with nowait exec ('use '+ @dbname + ' exec dbo.sp_vupgrade_subscription_tables') end else begin raiserror( 21373, 10, 1, @dbname) with nowait end fetch next from current_db into @dbname, @has_dbaccess end close current_db deallocate current_db -- -- all done -- go set ANSI_NULLS off go use master go dump tran master with no_log go checkpoint go -------------------------------------------------------------------------------- --. sp_MSmakectsview -------------------------------------------------------------------------------- if exists (select * from sysobjects where type = 'P' and name = 'sp_MSmakectsview') drop procedure sp_MSmakectsview go raiserror('Creating procedure sp_MSmakectsview', 0,1) GO create procedure sp_MSmakectsview @publication sysname, @ctsview sysname, @dynamic_snapshot_views_table_name sysname = null AS set nocount on declare @pubid uniqueidentifier declare @artid uniqueidentifier declare @pubidstr nvarchar(40) declare @artidstr nvarchar(40) declare @objid int declare @tablenick int declare @new_inactive int declare @new_active int declare @tablenickstr nvarchar(12) declare @command_piece nvarchar(2000) declare @rowguidcolname nvarchar(140) declare @view_type int declare @view_name nvarchar(270) declare @or_after_first nvarchar(100) declare @select_command nvarchar(4000) declare @retcode int declare @generate_per_article bit declare @newidstr nvarchar(40) declare @newid uniqueidentifier /* ** Security Check */ exec @retcode = sp_MSreplcheck_publish if @retcode <> 0 or @@error <> 0 return 1 /* By default the @generate_per_article is OFF */ set @generate_per_article = 0 set @new_inactive = 5 /* value of SQLDMOArtStat_New_Inactive */ set @new_active = 6 /* value of SQLDMOArtStat_New_Active */ if @ctsview IS NULL set @generate_per_article = 1 select @retcode = 0 set @or_after_first = '' select @pubid = pubid from sysmergepublications where name = @publication and UPPER(publisher)=UPPER(@@SERVERNAME) and publisher_db=db_name() if @pubid is null BEGIN RAISERROR (20026, 16, -1, @publication) RETURN (1) END select @newid = newid() exec @retcode = dbo.sp_MSguidtostr @newid, @newidstr out if @@ERROR <>0 OR @retcode <> 0 return (1) exec @retcode = dbo.sp_MSguidtostr @pubid, @pubidstr out if @@ERROR <>0 OR @retcode <> 0 return (1) -- create the temp tables here since the views are commong to both -- filtered and unfiltered per article contents views /* create temp table to insert into and select commands out of */ if @generate_per_article = 1 begin /* create temp table to insert into and select commands out of */ declare @temp_cts_views table ( step int identity NOT NULL, ctsvw sysname collate database_default null, tablenickname int ) if @@ERROR <> 0 return (1) select @tablenick = min(nickname) from dbo.sysmergearticles where pubid = @pubid and status<>@new_active and status<>@new_inactive while @tablenick is not null begin select @artid = artid from dbo.sysmergearticles where nickname = @tablenick and pubid = @pubid exec @retcode = dbo.sp_MSguidtostr @artid, @artidstr out if @@ERROR <>0 OR @retcode <> 0 return (1) set @ctsview = 'cont_' + @newidstr + '_' + @artidstr if exists (select * from sysobjects where name=@ctsview and type='V') begin declare @ownername sysname declare @viewname sysname select @ownername = user_name(uid) from sysobjects where name=@ctsview select @viewname = QUOTENAME(@ownername) + '.' + QUOTENAME(@ctsview) exec ('drop view ' + @viewname) if @@ERROR<>0 return (1) END -- insert the view name into the temp table created in this proc insert into @temp_cts_views (ctsvw, tablenickname) values(@ctsview, @tablenick) /* Advance to next article and repeat the loop */ select @tablenick = min(nickname) from dbo.sysmergearticles where pubid = @pubid and nickname > @tablenick and status<>@new_active and status<>@new_inactive end end else begin -- this is the regular case (called from sp_MSmakesystableviews) declare @tempcmd table ( phase int NOT NULL, step int identity NOT NULL, cmdtext nvarchar(4000) collate database_default null ) end /* pubidstr is needed in GUID format */ set @pubidstr = '''' + convert(nchar(36), @pubid) + '''' /* Check for the no filtering cases */ if not exists (select * from sysmergesubsetfilters where pubid = @pubid) and not exists (select * from sysmergearticles where pubid = @pubid and len(subset_filterclause) > 0) begin -- un filtered case if @generate_per_article = 0 begin -- If @generate_per_article = 0, an entire view is returned in @command_piece. set @command_piece = 'create view ' + @ctsview + ' as select * from dbo.MSmerge_contents ' --5 and 6 are the new article statuses - they indicate new_inactive and new_active set @command_piece = @command_piece + ' where tablenick in (select nickname from dbo.sysmergearticles where status<>5 and status<>6 and pubid = ' + @pubidstr + ')' insert into @tempcmd (phase, cmdtext) values (1, @command_piece) end else begin -- per article contents view for unfiltered publication select @tablenick = min(nickname) from dbo.sysmergearticles where pubid = @pubid and status<>@new_active and status<>@new_inactive while @tablenick is not null begin if not exists(select * from @temp_cts_views where tablenickname = @tablenick) return (1) select @ctsview = ctsvw from @temp_cts_views where tablenickname = @tablenick set @tablenickstr = convert(nchar(12), @tablenick) set @command_piece = 'create view ' + @ctsview + ' as select * from dbo.MSmerge_contents ' set @command_piece = @command_piece + ' where tablenick = ' + @tablenickstr exec @retcode= dbo.sp_executesql @command_piece if @@error <> 0 or @retcode <> 0 return 1 /* Advance to next article and repeat the loop */ select @tablenick = min(nickname) from dbo.sysmergearticles where pubid = @pubid and nickname > @tablenick and status<>@new_active and status<>@new_inactive end end goto Finish end -- filtered case if @generate_per_article = 0 begin set @command_piece = 'create view ' + @ctsview + ' as select * from dbo.MSmerge_contents where ' insert into @tempcmd (phase, cmdtext) values (1, @command_piece) end /* Initialize for loop over articles in this publication */ select @tablenick = min(nickname) from dbo.sysmergearticles where pubid = @pubid and status<>@new_active and status<>@new_inactive while @tablenick is not null begin if @dynamic_snapshot_views_table_name is null or @dynamic_snapshot_views_table_name = N'' begin select @objid = objid, @view_type = view_type, @view_name = object_name(sync_objid) from sysmergearticles where pubid = @pubid and nickname = @tablenick end else begin select @select_command = ' select @objid = sma.objid, @view_type = sma.view_type, @view_name = dsvt.dynamic_snapshot_view_name from sysmergearticles sma inner join ' + @dynamic_snapshot_views_table_name + ' dsvt on dsvt.artid = sma.artid where pubid = @pubid and nickname = @tablenick' exec sp_executesql @select_command, N'@objid int output, @view_type int output, @view_name nvarchar(270) output, @pubid uniqueidentifier, @tablenick int', @objid = @objid output, @view_type = @view_type output, @view_name = @view_name output, @pubid = @pubid, @tablenick = @tablenick if @@error<>0 return(1) end select @rowguidcolname = name from syscolumns where id = @objid and columnproperty(id, name, 'isrowguidcol')=1 set @rowguidcolname = QUOTENAME(@rowguidcolname) set @view_name = QUOTENAME(@view_name) set @tablenickstr = convert(nchar(12), @tablenick) -- if view_type is non-zero it means that we have a filtered article -- if view_type is 0 it means that we have an unfiltered article. -- the selects we do for the ctsview has to be different for these two cases if @generate_per_article = 0 begin if @view_type <> 0 begin set @command_piece = @or_after_first + ' (tablenick = ' + @tablenickstr + ' and rowguid in (select ' + @rowguidcolname + ' from ' + @view_name + '))' end else begin set @command_piece = @or_after_first + ' (tablenick = ' + @tablenickstr + ')' end insert into @tempcmd (phase, cmdtext) values (2, @command_piece) end else begin -- by the time we reach this point we have already pregenerated the view names that should -- be used and stored them in the temp table @temp_cts_views -- check here to make sure that this article exists in the temp table if not exists(select * from @temp_cts_views where tablenickname = @tablenick) return (1) select @ctsview = ctsvw from @temp_cts_views where tablenickname = @tablenick if @view_type <> 0 begin set @command_piece = 'create view dbo.' + @ctsview + ' as select * from dbo.MSmerge_contents where (tablenick = ' + @tablenickstr + ' and rowguid in (select ' + @rowguidcolname + ' from ' + @view_name + ')) ' exec @retcode= dbo.sp_executesql @command_piece if @@ERROR <>0 return (1) end else begin -- we get here if we find an unfiltered article in a publication which has some -- subset filters set @command_piece = 'create view dbo.' + @ctsview + ' as select * from dbo.MSmerge_contents where (tablenick = ' + @tablenickstr + ')' exec @retcode= dbo.sp_executesql @command_piece if @@ERROR <>0 return (1) end end -- end @generate_per_article check /* Advance to next article and repeat the loop */ select @tablenick = min(nickname) from sysmergearticles where pubid = @pubid and nickname > @tablenick and status<>@new_active and status<>@new_inactive /* make it so that any subsequent selects in the view are preceded by the word UNION */ /* using OR to replace 'UNION ALL', which is equivalent to workaround a bug */ set @or_after_first = ' OR ' end Finish: /* final steps: select out the text and drop the temp table */ if @generate_per_article = 0 begin select cmdtext from @tempcmd order by phase, step end else begin /* Select the view names so that the caller can query them so they can be BCP'd out and dropped later */ -- to see how this is read and used look at CMergePublication::GenerateContentsBcpFile select ctsvw from @temp_cts_views order by step end return(0) go exec dbo.sp_MS_marksystemobject sp_MSmakectsview go grant exec on dbo.sp_MSmakectsview to public go -------------------------------------------------------------------------------- --. sp_MSmakesystableviews -- performance optimization for zero rows in MSmerge_contents -------------------------------------------------------------------------------- if exists (select * from sysobjects where type = 'P' and name = 'sp_MSmakesystableviews') drop procedure sp_MSmakesystableviews go raiserror('Creating procedure sp_MSmakesystableviews', 0,1) GO -- Used by snapshot create procedure sp_MSmakesystableviews ( @publication sysname, @dynamic_snapshot_views_table_name sysname = null ) AS declare @guidstr nvarchar(40) declare @pubid uniqueidentifier declare @contentsview sysname declare @tombstoneview sysname declare @genhistoryview sysname declare @filtersview sysname declare @piece nvarchar(4000) declare @retcode smallint declare @dbname sysname declare @art_count int declare @skip_ctsv int declare @command nvarchar(500) declare @dynamic_filters bit declare @view_creation_command nvarchar(4000) declare @newid uniqueidentifier /* ** Check to see if current publication has permission */ exec @retcode=sp_MSreplcheck_publish if @retcode<>0 or @@ERROR<>0 return (1) set @skip_ctsv = 0 select @pubid = pubid, @dynamic_filters = dynamic_filters from sysmergepublications where name = @publication and UPPER(publisher)=UPPER(@@SERVERNAME) and publisher_db=db_name() if @pubid is null BEGIN RAISERROR (20026, 16, -1, @publication) RETURN (1) END select @art_count=count(*) from sysmergearticles where pubid=@pubid if @art_count > 253 set @skip_ctsv=1 select @newid = newid() create table #temp_table_for_systable_view(contentsview sysname, tombstoneview sysname NULL, genhistoryview sysname NULL, filtersview sysname NULL) exec @retcode = dbo.sp_MSguidtostr @newid, @guidstr out if @@ERROR<>0 OR @retcode<>0 return (1) select @contentsview = 'cont' + @guidstr select @tombstoneview = 'ts' + @guidstr select @genhistoryview = 'gh' + @guidstr select @filtersview = 'filt' + @guidstr set @guidstr = '''' + convert(nchar(36), @pubid) + '''' exec @retcode = dbo.sp_MSuniqueobjectname @tombstoneview, @tombstoneview out if @@ERROR<>0 OR @retcode<>0 return (1) exec @retcode = dbo.sp_MSuniqueobjectname @contentsview, @contentsview out if @@ERROR<>0 OR @retcode<>0 return (1) exec @retcode = dbo.sp_MSuniqueobjectname @genhistoryview, @genhistoryview out if @@ERROR<>0 OR @retcode<>0 return (1) exec @retcode = dbo.sp_MSuniqueobjectname @filtersview, @filtersview out if @@ERROR<>0 OR @retcode<>0 return (1) insert #temp_table_for_systable_view values(@contentsview,@tombstoneview,@genhistoryview,@filtersview) -- skip cts view if MSmerge_Contents contains zero rows if not exists (select * from MSmerge_contents) select @skip_ctsv = 1 if @skip_ctsv = 0 begin /* generate view for MSmerge_contents qualified by the pubid */ /* For dynamically filtered publication, security check is performed in the sync view of the base table */ set @command = 'sp_MSmakectsview ' + QUOTENAME(@publication) + ' , ' + @contentsview + ' , ' + COALESCE(QUOTENAME(@dynamic_snapshot_views_table_name) collate database_default, N'null' collate database_default) set @dbname = db_name() exec @retcode = master..xp_execresultset @command, @dbname if @@ERROR<>0 OR @retcode <>0 return (1) end else begin exec('create view ' + @contentsview + ' as select * from MSmerge_contents where 1 = 2') if @@ERROR<>0 return (1) end /* ** generate the view for dbo.MSmerge_tombstone. In SP2 and Shiloh, the change was made to make the view ** return 0 rows since it is unnecessary and expensive to propagate the tombstones. ** In order to leave all the other moving parts unchanged, we decided to let the view ** return 0 rows. */ select @view_creation_command = 'create view ' + @tombstoneview + ' as select * from dbo.MSmerge_tombstone where 1= 2 and tablenick in (select nickname from sysmergearticles where pubid = ' + @guidstr + ')' if @dynamic_filters = 1 begin select @view_creation_command = @view_creation_command + ' and ((is_srvrolemember(''sysadmin'') = 1) or (is_member(''db_owner'') = 1) or (sessionproperty(''replication_agent'') = 1))' end exec (@view_creation_command) if @@ERROR <>0 begin return (1) end select @view_creation_command = 'create view ' + @genhistoryview + '(guidsrc, guidlocal, pubid, generation, art_nick, nicknames, coldate) as select DISTINCT guidsrc, guidlocal, CONVERT(uniqueidentifier, ' + @guidstr + '), generation, art_nick, nicknames, coldate from dbo.MSmerge_genhistory where guidlocal <> ''00000000-0000-0000-0000-000000000000'' and (art_nick = 0 or art_nick is NULL or art_nick in (select nickname from sysmergearticles where pubid = ' + @guidstr + ')) ' if @dynamic_filters = 1 begin select @view_creation_command = @view_creation_command + ' and ((is_srvrolemember(''sysadmin'') = 1) or (is_member(''db_owner'') = 1) or (sessionproperty(''replication_agent'') = 1))' end exec (@view_creation_command) if @@ERROR <>0 begin return (1) end select @view_creation_command = 'create view ' + @filtersview + ' as select * from sysmergesubsetfilters where pubid = ' + @guidstr if @dynamic_filters = 1 begin select @view_creation_command = @view_creation_command + ' and ((is_srvrolemember(''sysadmin'') = 1) or (is_member(''db_owner'') = 1) or (sessionproperty(''replication_agent'') = 1))' end exec (@view_creation_command) if @@ERROR <>0 begin return (1) end if @dynamic_filters = 1 begin exec ('grant select on ' + @contentsview + ' to public') if @@error<>0 return(1) exec ('grant select on ' + @tombstoneview + ' to public') if @@error<>0 return(1) exec ('grant select on ' + @genhistoryview + ' to public') if @@error<>0 return(1) exec ('grant select on ' + @filtersview + ' to public') if @@error<>0 return(1) end set nocount on /* we only generate per-article contents view for static publications */ if @dynamic_filters=0 begin exec @retcode = sp_MSgettablecontents @pubid if @@ERROR<>0 OR @retcode <>0 return (1) end exec('select * from #temp_table_for_systable_view ') drop table #temp_table_for_systable_view return (0) go exec dbo.sp_MS_marksystemobject sp_MSmakesystableviews go grant exec on dbo.sp_MSmakesystableviews to public go -------------------------------------------------------------------------------- --. sp_MSvalidate_agent_parameter -- added the new parameter useperarticlecontentsview -------------------------------------------------------------------------------- if exists (select * from sysobjects where name = 'sp_MSvalidate_agent_parameter' and type = 'P') drop procedure sp_MSvalidate_agent_parameter raiserror('Creating procedure sp_MSvalidate_agent_parameter', 0,1) go create procedure sp_MSvalidate_agent_parameter ( @profile_id int, @parameter_name sysname, @parameter_value nvarchar(255) ) as declare @agent_type int declare @original_parameter_name sysname declare @numeric_value int -- Make sure parameters are non-null if @profile_id is null BEGIN RAISERROR (14043, 16, -1, '@profile_id') RETURN (1) END if @parameter_name is null BEGIN RAISERROR (14043, 16, -1, '@parameter_name') RETURN (1) END IF @parameter_value is null BEGIN RAISERROR (14043, 16, -1, '@parameter_value') RETURN (1) END select @original_parameter_name = @parameter_name select @agent_type = agent_type from msdb..MSagent_profiles where profile_id = @profile_id -- Parameter name validation if (substring(@parameter_name, 1, 1) <> '/' and substring(@parameter_name, 1, 1) <> '-') begin return 1 end select @parameter_name = lower(substring(@parameter_name, 2, len(@parameter_name) - 1) collate SQL_Latin1_General_CP1_CS_AS) -- Snapshot agent - agent_type = 1 if (@agent_type = 1) begin if not @parameter_name in ( N'bcpbatchsize', N'historyverboselevel', N'logintimeout', N'maxbcpthreads', N'querytimeout', N'startqueuetimeout', N'maxnetworkoptimization', N'useperarticlecontentsview' ) begin raiserror(21111, 16, -1, @original_parameter_name) return 1 end end -- Logreader - agent_type = 2 else if (@agent_type =2) begin if not lower(@parameter_name collate SQL_Latin1_General_CP1_CS_AS) in ( N'historyverboselevel', N'logintimeout', N'pollinginterval', N'querytimeout', N'readbatchsize' ) begin raiserror(21112, 16, -1, @original_parameter_name) return 1 end end -- Distribution agent - agent_type = 3 else if (@agent_type = 3) begin if not @parameter_name in ( N'bcpbatchsize', N'commitbatchsize', N'commitbatchthreshold', N'historyverboselevel', N'logintimeout', N'maxbcpthreads', N'maxdeliveredtransactions', N'pollinginterval', N'querytimeout', N'transactionsperhistory', N'skiperrors', N'keepalivemessageinterval', N'useinprocloader' ) begin raiserror(21113, 16, -1, @original_parameter_name) return 1 end end -- Merge agent - agent_type = 4 else if (@agent_type = 4) begin if not @parameter_name in ( N'startqueuetimeout', N'pollinginterval', N'validateinterval', N'logintimeout', N'querytimeout', N'maxuploadchanges', N'maxdownloadchanges', N'uploadgenerationsperbatch', N'downloadgenerationsperbatch', N'uploadreadchangesperbatch', N'downloadreadchangesperbatch', N'uploadwritechangesperbatch', N'downloadwritechangesperbatch', N'validate', N'fastrowcount', N'historyverboselevel', N'changesperhistory', N'bcpbatchsize', N'numdeadlockretries', N'keepalivemessageinterval', N'srcthreads', N'destthreads', N'useinprocloader', N'metadataretentioncleanup' ) begin raiserror(21114, 16, -1, @original_parameter_name) return 1 end end -- Qreader agent - agent_type = 9 else if (@agent_type = 9) begin if not lower(@parameter_name collate SQL_Latin1_General_CP1_CS_AS) in ( N'resolverstate', N'sqlqueuemode', N'historyverboselevel', N'pollinginterval', N'logintimeout', N'querytimeout' ) begin raiserror(21112, 16, -1, @original_parameter_name) return 1 end end else if @agent_type is null begin raiserror (20066, 16, -1) -- profile not defined return 1 end else begin -- MSagent_parameters table corruption return 1 end -- Parameter value validation if (@parameter_name = N'bcpbatchsize') begin select @numeric_value = convert(int, @parameter_value) if @@error <> 0 or @numeric_value < 1 begin raiserror(21115, 16, -1, @parameter_value, @original_parameter_name) return 1 end end else if (@parameter_name = N'commitbatchsize') begin select @numeric_value = convert(int, @parameter_value) if @@error <> 0 or @numeric_value < 1 begin raiserror(21115, 16, -1, @parameter_value, @original_parameter_name) return 1 end end else if (@parameter_name = N'commitbatchthreshold') begin select @numeric_value = convert(int, @parameter_value) if @@error <> 0 or @numeric_value < 1 begin raiserror(21115, 16, -1, @parameter_value, @original_parameter_name) return 1 end end else if (@parameter_name = N'downloadgenerationsperbatch') begin select @numeric_value = convert(int, @parameter_value) if @@error <> 0 or @numeric_value < 1 begin raiserror(21115, 16, -1, @parameter_value, @original_parameter_name) return 1 end if @numeric_value > 2000 begin raiserror(14266, 16, -1, 'DownloadGenerationsPerBatch', '1 - 2000') return 1 end end else if (@parameter_name = N'downloadreadchangesperbatch') begin select @numeric_value = convert(int, @parameter_value) if @@error <> 0 or @numeric_value < 1 begin raiserror(21115, 16, -1, @parameter_value, @original_parameter_name) return 1 end if @numeric_value > 2000 begin raiserror(14266, 16, -1, 'DownloadReadChangesPerBatch', '1 - 2000') return 1 end end else if (@parameter_name = N'downloadwritechangesperbatch') begin select @numeric_value = convert(int, @parameter_value) if @@error <> 0 or @numeric_value < 1 begin raiserror(21115, 16, -1, @parameter_value, @original_parameter_name) return 1 end if @numeric_value > 2000 begin raiserror(14266, 16, -1, 'DownloadWriteChangesPerBatch', '1 - 2000') return 1 end end else if (@parameter_name = N'fastrowcount') begin select @numeric_value = convert(int, @parameter_value) if @@error <> 0 or not (@numeric_value in (1,2,3)) begin raiserror(21116, 16, -1, @parameter_value, @original_parameter_name) return 1 end end else if (@parameter_name = N'historyverboselevel') begin select @numeric_value = convert(int, @parameter_value) if @@error <> 0 or not (@numeric_value in (0,1,2,3)) begin raiserror(21117, 16, -1, @parameter_value, @original_parameter_name) return 1 end end else if (@parameter_name = N'logintimeout') begin select @numeric_value = convert(int, @parameter_value) if @@error <> 0 or @numeric_value < 1 begin raiserror(21115, 16, -1, @parameter_value, @original_parameter_name) return 1 end end else if (@parameter_name = N'maxbcpthreads') begin select @numeric_value = convert(int, @parameter_value) if @@error <> 0 or @numeric_value < 1 begin raiserror(21115, 16, -1, @parameter_value, @original_parameter_name) return 1 end end else if (@parameter_name = N'maxdeliveredtransactions') begin select @numeric_value = convert(int, @parameter_value) if @@error <> 0 or @numeric_value < 0 begin raiserror(21119, 16, -1, @parameter_value, @original_parameter_name) return 1 end end else if (@parameter_name = N'pollinginterval') begin select @numeric_value = convert(int, @parameter_value) if @@error <> 0 or @numeric_value < 1 begin raiserror(21115, 16, -1, @parameter_value, @original_parameter_name) return 1 end end else if (@parameter_name = N'querytimeout') begin select @numeric_value = convert(int, @parameter_value) if @@error <> 0 or @numeric_value < 1 begin raiserror(21115, 16, -1, @parameter_value, @original_parameter_name) return 1 end end else if (@parameter_name = N'readbatchsize') begin select @numeric_value = convert(int, @parameter_value) if @@error <> 0 or @numeric_value < 1 begin raiserror(21115, 16, -1, @parameter_value, @original_parameter_name) return 1 end end else if (@parameter_name = N'transactionsperhistory') begin select @numeric_value = convert(int, @parameter_value) if @@error <> 0 or @numeric_value not between 0 and 10000 begin raiserror(211118, 16, -1, @parameter_value, @original_parameter_name) return 1 end end else if (@parameter_name = N'uploadgenerationsperbatch') begin select @numeric_value = convert(int, @parameter_value) if @@error <> 0 or @numeric_value < 1 begin raiserror(21115, 16, -1, @parameter_value, @original_parameter_name) return 1 end if @numeric_value > 2000 begin raiserror(14266, 16, -1, 'UploadGenerationsPerBatch', '1 - 2000') return 1 end end else if (@parameter_name = N'uploadreadchangesperbatch') begin select @numeric_value = convert(int, @parameter_value) if @@error <> 0 or @numeric_value < 1 begin raiserror(21115, 16, -1, @parameter_value, @original_parameter_name) return 1 end if @numeric_value > 2000 begin raiserror(14266, 16, -1, 'UploadReadChangesPerBatch', '1 - 2000') return 1 end end else if (@parameter_name = N'uploadwritechangesperbatch') begin select @numeric_value = convert(int, @parameter_value) if @@error <> 0 or @numeric_value < 1 begin raiserror(21115, 16, -1, @parameter_value, @original_parameter_name) return 1 end if @numeric_value > 2000 begin raiserror(14266, 16, -1, 'UploadWriteChangesPerBatch', '1 - 2000') return 1 end end else if (@parameter_name = N'validate') begin select @numeric_value = convert(int, @parameter_value) if @@error <> 0 or not (@numeric_value in (0,1,2,3)) begin raiserror(21117, 16, -1, @parameter_value, @original_parameter_name) return 1 end end else if (@parameter_name = N'validateinterval') begin select @numeric_value = convert(int, @parameter_value) if @@error <> 0 or @numeric_value < 1 begin raiserror(21115, 16, -1, @parameter_value, @original_parameter_name) return 1 end end else if (@parameter_name = N'skiperrors') begin -- Empty string is valid. if @parameter_value <> N'' begin -- Valid format: 11:22:33 if @parameter_value like '%[^0-9:]%' or @parameter_value like ':%' or @parameter_value like '%:' or @parameter_value like '%::%' begin raiserror(20601, 16, -1) return 1 end -- cannot has number of errors equals to or more than 11 if @parameter_value like '%:%:%:%:%:%:%:%:%:%:%' begin raiserror(20602, 16, -1) return 1 end end end -- Parameter value validation else if (@parameter_name = N'numdeadlockretries') begin select @numeric_value = convert(int, @parameter_value) if @@error <> 0 or @numeric_value < 1 begin raiserror(21115, 16, -1, @parameter_value, @original_parameter_name) return 1 end if @numeric_value > 100 begin raiserror(14266, 16, -1, 'NumDeadlockRetries', '1 - 100') return 1 end end else if (@parameter_name = N'srcthreads') begin select @numeric_value = convert(int, @parameter_value) if @@error <> 0 or @numeric_value < 1 begin raiserror(21115, 16, -1, @parameter_value, @original_parameter_name) return 1 end end else if (@parameter_name = N'destthreads') begin select @numeric_value = convert(int, @parameter_value) if @@error <> 0 or @numeric_value < 1 begin raiserror(21115, 16, -1, @parameter_value, @original_parameter_name) return 1 end end else if (@parameter_name = N'keepalivemessageinterval') begin select @numeric_value = convert(int, @parameter_value) if @@error <> 0 or @numeric_value < 30 begin raiserror(21405, 16, -1, @parameter_value, @original_parameter_name, 30) return 1 end end else if (@parameter_name = N'useinprocloader') begin select @numeric_value = convert(int, @parameter_value) if @@error <> 0 or @numeric_value not in (0, 1) or rtrim(@parameter_value) = N'' begin raiserror(21406, 16, -1, @parameter_value, @original_parameter_name) return 1 end end else if (@parameter_name = N'startqueuetimeout') begin select @numeric_value = convert(int, @parameter_value) if @@error <> 0 or (@numeric_value < 300 and @numeric_value <> 0) or rtrim(@parameter_value) = N'' begin raiserror(21404, 16, -1, @parameter_value, @original_parameter_name) return 1 end end else if (@parameter_name = N'resolverstate') begin select @numeric_value = convert(int, @parameter_value) if @@error <> 0 or @numeric_value not in (1,2,3) begin raiserror(21115, 16, -1, @parameter_value, @original_parameter_name) return 1 end end else if (@parameter_name = N'sqlqueuemode') begin select @numeric_value = convert(int, @parameter_value) if @@error <> 0 or @numeric_value not in (0,1,2) begin raiserror(21115, 16, -1, @parameter_value, @original_parameter_name) return 1 end end else if (@parameter_name = N'maxnetworkoptimization') begin select @numeric_value = convert(int, @parameter_value) if @@error <> 0 or @numeric_value not in (0, 1) or rtrim(@parameter_value) = N'' begin raiserror(21406, 16, -1, @parameter_value, @original_parameter_name) return 1 end end else if (@parameter_name = N'useperarticlecontentsview') begin select @numeric_value = convert(int, @parameter_value) if @@error <> 0 or @numeric_value not in (0, 1) or rtrim(@parameter_value) = N'' begin raiserror(21406, 16, -1, @parameter_value, @original_parameter_name) return 1 end end return 0 go EXEC dbo.sp_MS_marksystemobject 'sp_MSvalidate_agent_parameter' go -- Revoke privileges on mswebtasks and stored procedures USE msdb go REVOKE INSERT ON mswebtasks FROM PUBLIC go REVOKE DELETE ON mswebtasks FROM PUBLIC go REVOKE UPDATE ON mswebtasks FROM PUBLIC go REVOKE EXECUTE ON sp_insmswebtask FROM PUBLIC go REVOKE EXECUTE ON sp_updmswebtask FROM PUBLIC go -- Revoke privileges on stored procedures USE master go REVOKE EXECUTE ON sp_makewebtask FROM PUBLIC go REVOKE EXECUTE ON sp_dropwebtask FROM PUBLIC go REVOKE EXECUTE ON sp_cleanupwebtask FROM PUBLIC go